summaryrefslogtreecommitdiffstats
path: root/avahi-core
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2005-09-25 20:12:40 +0000
committerLennart Poettering <lennart@poettering.net>2005-09-25 20:12:40 +0000
commit1ffedb586bd2fb6daa3970304fac7c5b415cd38f (patch)
treef084dcd2594490b7e95ab026ad2efeaeab3f998a /avahi-core
parent5867849876e19996fd05a0d4917cb739904519c1 (diff)
* split off lookup.h and publish.h from core.h
* implement wide-area DNS-SD * if multiple clients query the same records, only start the query packet sequence once * implement recursive CNAME queries * add support for resolving services without TXT or A/AAAA records * enlarge resolving timeouts to 5s * implement new browse/resolving events CACHE_EXHAUSTED/ALL_FOR_NOW * add support for resolving services without name. (i.e. for normal SRV records) git-svn-id: file:///home/lennart/svn/public/avahi/trunk@608 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
Diffstat (limited to 'avahi-core')
-rw-r--r--avahi-core/Makefile.am20
-rw-r--r--avahi-core/announce.c2
-rw-r--r--avahi-core/announce.h1
-rw-r--r--avahi-core/avahi-test.c179
-rw-r--r--avahi-core/browse-dns-server.c187
-rw-r--r--avahi-core/browse-domain.c56
-rw-r--r--avahi-core/browse-service-type.c83
-rw-r--r--avahi-core/browse-service.c81
-rw-r--r--avahi-core/browse.c646
-rw-r--r--avahi-core/browse.h34
-rw-r--r--avahi-core/cache.c10
-rw-r--r--avahi-core/conformance-test.c3
-rw-r--r--avahi-core/core.h427
-rw-r--r--avahi-core/dns.c33
-rw-r--r--avahi-core/dns.h10
-rw-r--r--avahi-core/hashmap.c16
-rw-r--r--avahi-core/iface.c23
-rw-r--r--avahi-core/iface.h6
-rw-r--r--avahi-core/lookup.h284
-rw-r--r--avahi-core/multicast-lookup.c346
-rw-r--r--avahi-core/multicast-lookup.h53
-rw-r--r--avahi-core/publish.h244
-rw-r--r--avahi-core/querier-test.c123
-rw-r--r--avahi-core/querier.c200
-rw-r--r--avahi-core/querier.h50
-rw-r--r--avahi-core/resolve-address.c176
-rw-r--r--avahi-core/resolve-host-name.c199
-rw-r--r--avahi-core/resolve-service.c463
-rw-r--r--avahi-core/rr.c13
-rw-r--r--avahi-core/rr.h3
-rw-r--r--avahi-core/server.c106
-rw-r--r--avahi-core/server.h7
-rw-r--r--avahi-core/socket.c144
-rw-r--r--avahi-core/socket.h5
-rw-r--r--avahi-core/wide-area.c693
-rw-r--r--avahi-core/wide-area.h54
36 files changed, 3861 insertions, 1119 deletions
diff --git a/avahi-core/Makefile.am b/avahi-core/Makefile.am
index 59b6215..9a2c098 100644
--- a/avahi-core/Makefile.am
+++ b/avahi-core/Makefile.am
@@ -27,7 +27,9 @@ avahiincludedir=$(includedir)/avahi-core
avahiinclude_HEADERS = \
core.h \
log.h \
- rr.h
+ rr.h \
+ publish.h \
+ lookup.h
lib_LTLIBRARIES = \
libavahi-core.la
@@ -39,7 +41,8 @@ noinst_PROGRAMS = \
avahi-reflector \
dns-test \
timeeventq-test \
- hashmap-test
+ hashmap-test \
+ querier-test
libavahi_core_la_SOURCES = \
timeeventq.c timeeventq.h\
@@ -63,12 +66,15 @@ libavahi_core_la_SOURCES = \
resolve-service.c \
dns.c dns.h \
rr.c rr.h \
- core.h \
+ core.h lookup.h publish.h \
log.c log.h \
browse-dns-server.c \
fdutil.h fdutil.c \
util.c util.h \
- hashmap.c hashmap.h
+ hashmap.c hashmap.h \
+ wide-area.c wide-area.h \
+ multicast-lookup.c multicast-lookup.h \
+ querier.c querier.h
libavahi_core_la_CFLAGS = $(AM_CFLAGS)
libavahi_core_la_LIBADD = $(AM_LDADD) ../avahi-common/libavahi-common.la
@@ -85,6 +91,12 @@ avahi_test_SOURCES = \
avahi_test_CFLAGS = $(AM_CFLAGS)
avahi_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la
+
+querier_test_SOURCES = \
+ querier-test.c
+querier_test_CFLAGS = $(AM_CFLAGS)
+querier_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la
+
conformance_test_SOURCES = \
conformance-test.c
conformance_test_CFLAGS = $(AM_CFLAGS)
diff --git a/avahi-core/announce.c b/avahi-core/announce.c
index c6edd83..c21b147 100644
--- a/avahi-core/announce.c
+++ b/avahi-core/announce.c
@@ -443,7 +443,7 @@ void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int goodbye) {
/* avahi_log_debug("goodbye entry: %p", e); */
if (goodbye && !e->dead)
- avahi_interface_monitor_walk(s->monitor, 0, AF_UNSPEC, send_goodbye_callback, e);
+ avahi_interface_monitor_walk(s->monitor, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, send_goodbye_callback, e);
while (e->announcements)
remove_announcement(s, e->announcements);
diff --git a/avahi-core/announce.h b/avahi-core/announce.h
index 9c01d26..9183258 100644
--- a/avahi-core/announce.h
+++ b/avahi-core/announce.h
@@ -28,6 +28,7 @@ typedef struct AvahiAnnouncement AvahiAnnouncement;
#include "iface.h"
#include "server.h"
#include "timeeventq.h"
+#include "publish.h"
typedef enum {
AVAHI_PROBING,
diff --git a/avahi-core/avahi-test.c b/avahi-core/avahi-test.c
index db0d685..0490c84 100644
--- a/avahi-core/avahi-test.c
+++ b/avahi-core/avahi-test.c
@@ -35,6 +35,8 @@
#include <avahi-common/alternative.h>
#include <avahi-core/core.h>
#include <avahi-core/log.h>
+#include <avahi-core/publish.h>
+#include <avahi-core/lookup.h>
static AvahiSEntryGroup *group = NULL;
static AvahiServer *server = NULL;
@@ -62,18 +64,47 @@ static void dump_timeout_callback(AvahiTimeout *timeout, void* userdata) {
poll_api->timeout_update(timeout, &tv);
}
-static void record_browser_callback(AvahiSRecordBrowser *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) {
+static const char *browser_event_to_string(AvahiBrowserEvent event) {
+ switch (event) {
+ case AVAHI_BROWSER_NEW : return "NEW";
+ case AVAHI_BROWSER_REMOVE : return "REMOVE";
+ case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CACHE_EXHAUSTED";
+ case AVAHI_BROWSER_ALL_FOR_NOW : return "ALL_FOR_NOW";
+ case AVAHI_BROWSER_FAILURE : return "FAILURE";
+ case AVAHI_BROWSER_NOT_FOUND : return "NOT_FOUND";
+ }
+
+ abort();
+}
+
+static const char *resolver_event_to_string(AvahiResolverEvent event) {
+ switch (event) {
+ case AVAHI_RESOLVER_FOUND: return "FOUND";
+ case AVAHI_RESOLVER_TIMEOUT: return "TIMEOUT";
+ case AVAHI_RESOLVER_NOT_FOUND: return "NOT_FOUND";
+ case AVAHI_RESOLVER_FAILURE: return "FAILURE";
+ }
+ abort();
+}
+
+static void record_browser_callback(
+ AvahiSRecordBrowser *r,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
char *t;
assert(r);
- assert(record);
- assert(interface > 0);
- assert(protocol != AVAHI_PROTO_UNSPEC);
- avahi_log_debug("SUBSCRIPTION: record [%s] on %i.%i is %s", t = avahi_record_to_string(record), interface, protocol,
- event == AVAHI_BROWSER_NEW ? "new" : "remove");
+ if (record) {
+ avahi_log_debug("RB: record [%s] on %i.%i is %s", t = avahi_record_to_string(record), interface, protocol, browser_event_to_string(event));
+ avahi_free(t);
+ } else
+ avahi_log_debug("RB: [%s]", browser_event_to_string(event));
- avahi_free(t);
}
static void remove_entries(void);
@@ -96,7 +127,7 @@ static void server_callback(AvahiServer *s, AvahiServerState state, void* userda
avahi_log_debug("server state: %i", state);
if (state == AVAHI_SERVER_RUNNING) {
- avahi_log_debug("Server startup complete. Host name is <%s>", avahi_server_get_host_name_fqdn(s));
+ avahi_log_debug("Server startup complete. Host name is <%s>. Service cookie is %u", avahi_server_get_host_name_fqdn(s), avahi_server_get_local_service_cookie(s));
create_entries(0);
} else if (state == AVAHI_SERVER_COLLISION) {
char *n;
@@ -163,56 +194,122 @@ fail:
group = NULL;
}
-static void hnr_callback(AvahiSHostNameResolver *r, AvahiIfIndex iface, AvahiProtocol protocol, AvahiResolverEvent event, const char *hostname, const AvahiAddress *a, void* userdata) {
+static void hnr_callback(
+ AvahiSHostNameResolver *r,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *hostname,
+ const AvahiAddress *a,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
char t[64];
if (a)
avahi_address_snprint(t, sizeof(t), a);
- avahi_log_debug("HNR: (%i.%i) <%s> -> %s [%s]", iface, protocol, hostname, a ? t : "n/a", event == AVAHI_RESOLVER_FOUND ? "found" : "timeout");
+ avahi_log_debug("HNR: (%i.%i) <%s> -> %s [%s]", iface, protocol, hostname, a ? t : "n/a", resolver_event_to_string(event));
}
-static void ar_callback(AvahiSAddressResolver *r, AvahiIfIndex iface, AvahiProtocol protocol, AvahiResolverEvent event, const AvahiAddress *a, const char *hostname, void* userdata) {
+static void ar_callback(
+ AvahiSAddressResolver *r,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const AvahiAddress *a,
+ const char *hostname,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
char t[64];
avahi_address_snprint(t, sizeof(t), a);
- avahi_log_debug("AR: (%i.%i) %s -> <%s> [%s]", iface, protocol, t, hostname ? hostname : "n/a", event == AVAHI_RESOLVER_FOUND ? "found" : "timeout");
+ avahi_log_debug("AR: (%i.%i) %s -> <%s> [%s]", iface, protocol, t, hostname ? hostname : "n/a", resolver_event_to_string(event));
}
-static void db_callback(AvahiSDomainBrowser *b, AvahiIfIndex iface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *domain, void* userdata) {
+static void db_callback(
+ AvahiSDomainBrowser *b,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *domain,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
- avahi_log_debug("DB: (%i.%i) <%s> [%s]", iface, protocol, domain, event == AVAHI_BROWSER_NEW ? "new" : "remove");
+ avahi_log_debug("DB: (%i.%i) <%s> [%s]", iface, protocol, domain, browser_event_to_string(event));
}
-static void stb_callback(AvahiSServiceTypeBrowser *b, AvahiIfIndex iface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *service_type, const char *domain, void* userdata) {
-
- avahi_log_debug("STB: (%i.%i) %s in <%s> [%s]", iface, protocol, service_type, domain, event == AVAHI_BROWSER_NEW ? "new" : "remove");
+static void stb_callback(
+ AvahiSServiceTypeBrowser *b,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *service_type,
+ const char *domain,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ avahi_log_debug("STB: (%i.%i) %s in <%s> [%s]", iface, protocol, service_type, domain, browser_event_to_string(event));
}
-static void sb_callback(AvahiSServiceBrowser *b, AvahiIfIndex iface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *service_type, const char *domain, void* userdata) {
- avahi_log_debug("SB: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name, service_type, domain, event == AVAHI_BROWSER_NEW ? "new" : "remove");
+static void sb_callback(
+ AvahiSServiceBrowser *b,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *service_type,
+ const char *domain,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+ avahi_log_debug("SB: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name, service_type, domain, browser_event_to_string(event));
}
-static void sr_callback(AvahiSServiceResolver *r, AvahiIfIndex iface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char*service_type, const char*domain_name, const char*hostname, const AvahiAddress *a, uint16_t port, AvahiStringList *txt, void* userdata) {
-
- if (event == AVAHI_RESOLVER_TIMEOUT)
- avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s> [timeout]", iface, protocol, name, service_type, domain_name);
+static void sr_callback(
+ AvahiSServiceResolver *r,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *name,
+ const char*service_type,
+ const char*domain_name,
+ const char*hostname,
+ const AvahiAddress *a,
+ uint16_t port,
+ AvahiStringList *txt,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ if (event != AVAHI_RESOLVER_FOUND)
+ avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name, service_type, domain_name, resolver_event_to_string(event));
else {
char t[64], *s;
avahi_address_snprint(t, sizeof(t), a);
s = avahi_string_list_to_string(txt);
- avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s>: %s/%s:%i (%s) [found]", iface, protocol, name, service_type, domain_name, hostname, t, port, s);
+ avahi_log_debug("SR: (%i.%i) <%s> as %s in <%s>: %s/%s:%i (%s) [%s]", iface, protocol, name, service_type, domain_name, hostname, t, port, s, resolver_event_to_string(event));
avahi_free(s);
}
}
-static void dsb_callback(AvahiSDNSServerBrowser *b, AvahiIfIndex iface, AvahiProtocol protocol, AvahiBrowserEvent event, const char*hostname, const AvahiAddress *a, uint16_t port, void* userdata) {
- char t[64];
- avahi_address_snprint(t, sizeof(t), a);
- avahi_log_debug("DSB: (%i.%i): %s/%s:%i [%s]", iface, protocol, hostname, t, port, event == AVAHI_BROWSER_NEW ? "new" : "remove");
+static void dsb_callback(
+ AvahiSDNSServerBrowser *b,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char*hostname,
+ const AvahiAddress *a,
+ uint16_t port,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ char t[64] = "n/a";
+
+ if (a)
+ avahi_address_snprint(t, sizeof(t), a);
+
+ avahi_log_debug("DSB: (%i.%i): %s/%s:%i [%s]", iface, protocol, hostname, t, port, browser_event_to_string(event));
}
int main(int argc, char *argv[]) {
@@ -235,27 +332,31 @@ int main(int argc, char *argv[]) {
poll_api = avahi_simple_poll_get(simple_poll);
avahi_server_config_init(&config);
-/* config.host_name = g_strdup("test"); */
+
+ avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]);
+ config.n_wide_area_servers = 1;
+ config.enable_wide_area = 1;
+
server = avahi_server_new(poll_api, &config, server_callback, NULL, &error);
avahi_server_config_free(&config);
-
- k = avahi_key_new("_http._tcp.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
- r = avahi_s_record_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, k, record_browser_callback, NULL);
+
+ k = avahi_key_new("_http._tcp.0pointer.de", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
+ r = avahi_s_record_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, k, 0, record_browser_callback, NULL);
avahi_key_unref(k);
- hnr = avahi_s_host_name_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "codes-CompUTER.local", AVAHI_PROTO_UNSPEC, hnr_callback, NULL);
+ hnr = avahi_s_host_name_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "cocaine.local", AVAHI_PROTO_UNSPEC, 0, hnr_callback, NULL);
- ar = avahi_s_address_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, avahi_address_parse("192.168.50.15", AVAHI_PROTO_INET, &a), ar_callback, NULL);
+ ar = avahi_s_address_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, avahi_address_parse("192.168.50.1", AVAHI_PROTO_INET, &a), 0, ar_callback, NULL);
- db = avahi_s_domain_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, db_callback, NULL);
+ db = avahi_s_domain_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, db_callback, NULL);
- stb = avahi_s_service_type_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, stb_callback, NULL);
+ stb = avahi_s_service_type_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, 0, stb_callback, NULL);
- sb = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_http._tcp", NULL, sb_callback, NULL);
+ sb = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_http._tcp", NULL, 0, sb_callback, NULL);
- sr = avahi_s_service_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "Ecstasy HTTP", "_http._tcp", "local", AVAHI_PROTO_UNSPEC, sr_callback, NULL);
+ sr = avahi_s_service_resolver_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "Ecstasy HTTP", "_http._tcp", "local", AVAHI_PROTO_UNSPEC, 0, sr_callback, NULL);
- dsb = avahi_s_dns_server_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "local", AVAHI_DNS_SERVER_RESOLVE, AVAHI_PROTO_UNSPEC, dsb_callback, NULL);
+ dsb = avahi_s_dns_server_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "local", AVAHI_DNS_SERVER_RESOLVE, AVAHI_PROTO_UNSPEC, 0, dsb_callback, NULL);
avahi_elapse_time(&tv, 1000*5, 0);
poll_api->timeout_new(poll_api, &tv, dump_timeout_callback, server);
diff --git a/avahi-core/browse-dns-server.c b/avahi-core/browse-dns-server.c
index 9121150..675000b 100644
--- a/avahi-core/browse-dns-server.c
+++ b/avahi-core/browse-dns-server.c
@@ -43,6 +43,7 @@ struct AvahiDNSServerInfo {
AvahiRecord *srv_record;
AvahiSHostNameResolver *host_name_resolver;
AvahiAddress address;
+ AvahiLookupResultFlags flags;
AVAHI_LLIST_FIELDS(AvahiDNSServerInfo, info);
};
@@ -55,6 +56,7 @@ struct AvahiSDNSServerBrowser {
AvahiSDNSServerBrowserCallback callback;
void* userdata;
AvahiProtocol aprotocol;
+ AvahiLookupFlags user_flags;
unsigned n_info;
@@ -93,67 +95,157 @@ static void server_info_free(AvahiSDNSServerBrowser *b, AvahiDNSServerInfo *i) {
avahi_free(i);
}
-static void host_name_resolver_callback(AvahiSHostNameResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *host_name, const AvahiAddress *a, void* userdata) {
+static void host_name_resolver_callback(
+ AvahiSHostNameResolver *r,
+ AvahiIfIndex interface,
+ 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);
- if (event == 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->browser->userdata);
+ 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_NOT_FOUND:
+ case AVAHI_RESOLVER_FAILURE:
+ case AVAHI_RESOLVER_TIMEOUT:
+ /* 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, void* userdata) {
+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(record);
assert(b);
- assert(record->key->type == AVAHI_DNS_TYPE_SRV);
-
- if (event == AVAHI_BROWSER_NEW) {
- AvahiDNSServerInfo *i;
-
- 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_new(b->server, interface, protocol, record->data.srv.name, b->aprotocol, host_name_resolver_callback, i);
-
- AVAHI_LLIST_PREPEND(AvahiDNSServerInfo, info, b->info, i);
-
- b->n_info++;
- } else if (event == AVAHI_BROWSER_REMOVE) {
- AvahiDNSServerInfo *i;
-
- 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, b->userdata);
-
- server_info_free(b, i);
+ 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_new(
+ b->server,
+ interface, protocol,
+ record->data.srv.name,
+ b->aprotocol,
+ b->user_flags,
+ host_name_resolver_callback, i);
+ i->flags = flags;
+
+ 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_NOT_FOUND:
+ 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_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *domain, AvahiDNSServerType type, AvahiProtocol aprotocol, AvahiSDNSServerBrowserCallback callback, void* userdata) {
+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;
AvahiKey *k;
char *n = NULL;
@@ -166,18 +258,27 @@ AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(AvahiServer *server, Avah
avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME);
return NULL;
}
-
+
+ if (!domain)
+ domain = server->domain_name;
+
+ if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS);
+ return NULL;
+ }
+
if (!(b = avahi_new(AvahiSDNSServerBrowser, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
}
b->server = server;
- b->domain_name = avahi_normalize_name(domain ? domain : "local");
+ b->domain_name = avahi_normalize_name(domain);
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);
@@ -186,7 +287,7 @@ AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(AvahiServer *server, Avah
k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
avahi_free(n);
- b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, b);
+ b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b);
avahi_key_unref(k);
if (!b->record_browser) {
diff --git a/avahi-core/browse-domain.c b/avahi-core/browse-domain.c
index 03219e4..c5d2ba8 100644
--- a/avahi-core/browse-domain.c
+++ b/avahi-core/browse-domain.c
@@ -34,29 +34,48 @@ struct AvahiSDomainBrowser {
char *domain_name;
AvahiSRecordBrowser *record_browser;
-
+ AvahiLookupResultFlags flags;
+
AvahiSDomainBrowserCallback callback;
void* userdata;
AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser);
};
-static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) {
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
AvahiSDomainBrowser *b = userdata;
- char *n;
+ char *n = NULL;
assert(rr);
- assert(record);
assert(b);
-
- assert(record->key->type == AVAHI_DNS_TYPE_PTR);
-
- n = avahi_normalize_name(record->data.ptr.name);
- b->callback(b, interface, protocol, event, n, b->userdata);
+
+ if (record) {
+ assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+ n = avahi_normalize_name(record->data.ptr.name);
+ }
+
+ b->callback(b, interface, protocol, event, n, flags, b->userdata);
avahi_free(n);
}
-AvahiSDomainBrowser *avahi_s_domain_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *domain, AvahiDomainBrowserType type, AvahiSDomainBrowserCallback callback, void* userdata) {
+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;
AvahiKey *k;
char *n = NULL;
@@ -65,18 +84,31 @@ AvahiSDomainBrowser *avahi_s_domain_browser_new(AvahiServer *server, AvahiIfInde
assert(callback);
assert(type >= AVAHI_DOMAIN_BROWSER_BROWSE && type <= AVAHI_DOMAIN_BROWSER_BROWSE_LEGACY);
+ if (!AVAHI_IF_VALID(interface)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_INTERFACE);
+ return NULL;
+ }
+
if (domain && !avahi_is_valid_domain_name(domain)) {
avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME);
return NULL;
}
+ if (!domain)
+ domain = server->domain_name;
+
+ if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS);
+ return NULL;
+ }
+
if (!(b = avahi_new(AvahiSDomainBrowser, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
}
b->server = server;
- b->domain_name = avahi_normalize_name(domain ? domain : "local");
+ b->domain_name = avahi_normalize_name(domain);
b->callback = callback;
b->userdata = userdata;
@@ -109,7 +141,7 @@ AvahiSDomainBrowser *avahi_s_domain_browser_new(AvahiServer *server, AvahiIfInde
k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
avahi_free(n);
- b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, b);
+ b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b);
avahi_key_unref(k);
if (!b->record_browser) {
diff --git a/avahi-core/browse-service-type.c b/avahi-core/browse-service-type.c
index 896e46b..3ea28c0 100644
--- a/avahi-core/browse-service-type.c
+++ b/avahi-core/browse-service-type.c
@@ -44,40 +44,51 @@ struct AvahiSServiceTypeBrowser {
AVAHI_LLIST_FIELDS(AvahiSServiceTypeBrowser, browser);
};
-static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) {
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
AvahiSServiceTypeBrowser *b = userdata;
- char *n, *e, *c;
+ char *n = NULL, *c = NULL;
assert(rr);
- assert(record);
assert(b);
- assert(record->key->type == AVAHI_DNS_TYPE_PTR);
-
- n = avahi_normalize_name(record->data.ptr.name);
-
- if (*n != '_')
- goto fail;
-
- for (c = e = n; *c == '_';) {
- c += strcspn(c, ".");
-
- if (*c == 0)
+ if (record) {
+ char *e;
+
+ assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+ n = avahi_normalize_name(record->data.ptr.name);
+
+ if (*n != '_')
+ goto fail;
+
+ for (c = e = n; *c == '_';) {
+ c += strcspn(c, ".");
+
+ if (*c == 0)
+ goto fail;
+
+ assert(*c == '.');
+ e = c;
+ c++;
+ }
+
+ *e = 0;
+
+ if (!avahi_domain_equal(c, b->domain_name))
goto fail;
-
- assert(*c == '.');
- e = c;
- c++;
}
-
- *e = 0;
-
- if (!avahi_domain_equal(c, b->domain_name))
- goto fail;
- b->callback(b, interface, protocol, event, n, c, b->userdata);
+ b->callback(b, interface, protocol, event, n, c, flags, b->userdata);
avahi_free(n);
-
+
return;
fail:
@@ -85,7 +96,15 @@ fail:
avahi_free(n);
}
-AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *domain, AvahiSServiceTypeBrowserCallback callback, void* userdata) {
+AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *domain,
+ AvahiLookupFlags flags,
+ AvahiSServiceTypeBrowserCallback callback,
+ void* userdata) {
+
AvahiSServiceTypeBrowser *b;
AvahiKey *k;
char *n = NULL;
@@ -98,13 +117,21 @@ AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(AvahiServer *server,
return NULL;
}
+ if (!domain)
+ domain = server->domain_name;
+
+ if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS);
+ return NULL;
+ }
+
if (!(b = avahi_new(AvahiSServiceTypeBrowser, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
}
b->server = server;
- b->domain_name = avahi_normalize_name(domain ? domain : "local");
+ b->domain_name = avahi_normalize_name(domain);
b->callback = callback;
b->userdata = userdata;
@@ -114,7 +141,7 @@ AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(AvahiServer *server,
k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
avahi_free(n);
- b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, b);
+ b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b);
avahi_key_unref(k);
if (!b->record_browser)
diff --git a/avahi-core/browse-service.c b/avahi-core/browse-service.c
index 9419122..55fba6a 100644
--- a/avahi-core/browse-service.c
+++ b/avahi-core/browse-service.c
@@ -45,39 +45,49 @@ struct AvahiSServiceBrowser {
AVAHI_LLIST_FIELDS(AvahiSServiceBrowser, browser);
};
-static void record_browser_callback(AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, void* userdata) {
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
AvahiSServiceBrowser *b = userdata;
- char *n, *e, *c, *s;
+ char *n = NULL, *c = NULL, *s = NULL;
char service[128];
assert(rr);
- assert(record);
assert(b);
- assert(record->key->type == AVAHI_DNS_TYPE_PTR);
-
- c = n = avahi_normalize_name(record->data.ptr.name);
-
- if (!(avahi_unescape_label((const char**) &c, service, sizeof(service))))
- goto fail;
-
- for (s = e = c; *c == '_';) {
- c += strcspn(c, ".");
-
- if (*c == 0)
+ if (record) {
+ char *e;
+ assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+ c = n = avahi_normalize_name(record->data.ptr.name);
+
+ if (!(avahi_unescape_label((const char**) &c, service, sizeof(service))))
+ goto fail;
+
+ for (s = e = c; *c == '_';) {
+ c += strcspn(c, ".");
+
+ if (*c == 0)
+ goto fail;
+
+ assert(*c == '.');
+ e = c;
+ c++;
+ }
+
+ *e = 0;
+
+ if (!avahi_domain_equal(c, b->domain_name))
goto fail;
-
- assert(*c == '.');
- e = c;
- c++;
}
-
- *e = 0;
-
- if (!avahi_domain_equal(c, b->domain_name))
- goto fail;
- b->callback(b, interface, protocol, event, service, s, c, b->userdata);
+ b->callback(b, interface, protocol, event, record ? service : NULL, s, c, flags, b->userdata);
avahi_free(n);
return;
@@ -87,7 +97,16 @@ fail:
avahi_free(n);
}
-AvahiSServiceBrowser *avahi_s_service_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *service_type, const char *domain, AvahiSServiceBrowserCallback callback, void* userdata) {
+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;
AvahiKey *k;
char *n = NULL;
@@ -106,13 +125,21 @@ AvahiSServiceBrowser *avahi_s_service_browser_new(AvahiServer *server, AvahiIfIn
return NULL;
}
+ if (!domain)
+ domain = server->domain_name;
+
+ if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS);
+ 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 = avahi_normalize_name(domain ? domain : "local");
+ b->domain_name = avahi_normalize_name(domain);
b->service_type = avahi_normalize_name(service_type);
b->callback = callback;
b->userdata = userdata;
@@ -122,7 +149,7 @@ AvahiSServiceBrowser *avahi_s_service_browser_new(AvahiServer *server, AvahiIfIn
k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
avahi_free(n);
- b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, b);
+ b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b);
avahi_key_unref(k);
diff --git a/avahi-core/browse.c b/avahi-core/browse.c
index fa97c35..a6ba7ca 100644
--- a/avahi-core/browse.c
+++ b/avahi-core/browse.c
@@ -23,137 +23,512 @@
#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"
+
+#define AVAHI_MAX_LOOKUPS_PER_BROWSER 15
-struct AvahiSRecordBrowser {
- int dead;
+struct AvahiSRBLookup {
+ AvahiSRecordBrowser *record_browser;
- AvahiServer *server;
- AvahiKey *key;
+ unsigned ref;
+
AvahiIfIndex interface;
AvahiProtocol protocol;
- unsigned sec_delay;
-
- AvahiTimeEvent *query_time_event;
- AvahiTimeEvent *scan_time_event;
+ AvahiLookupFlags flags;
+
+ AvahiKey *key;
- AvahiSRecordBrowserCallback callback;
- void* userdata;
+ AvahiWideAreaLookup *wide_area;
+ AvahiMulticastLookup *multicast;
- AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, browser);
- AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, by_key);
+ AvahiRList *cname_lookups;
+
+ AVAHI_LLIST_FIELDS(AvahiSRBLookup, lookups);
};
-static void elapse_callback(AvahiTimeEvent *e, void *userdata) {
- AvahiSRecordBrowser *s = userdata;
- struct timeval tv;
-/* char *t; */
+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(s);
+ assert(b);
+ assert(AVAHI_IF_VALID(interface));
+ assert(AVAHI_PROTO_VALID(protocol));
- avahi_server_post_query(s->server, s->interface, s->protocol, s->key);
+ if (b->n_lookups >= AVAHI_MAX_LOOKUPS_PER_BROWSER)
+ /* 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);
- s->sec_delay *= 2;
+ b->n_lookups ++;
- if (s->sec_delay >= 60*60) /* 1h */
- s->sec_delay = 60*60;
+ 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;
+ }
-/* avahi_log_debug("Continuous querying for %s (%i)", t = avahi_key_to_string(s->key), s->sec_delay); */
-/* avahi_free(t); */
+ 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_elapse_time(&tv, s->sec_delay*1000, 0);
- avahi_time_event_update(s->query_time_event, &tv);
+ avahi_key_unref(l->key);
+ avahi_free(l);
}
-struct cbdata {
- AvahiSRecordBrowser *record_browser;
- AvahiInterface *interface;
-};
+static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) {
+ assert(l);
+ assert(l->ref >= 1);
-static void* scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
- struct cbdata *cbdata = userdata;
+ l->ref++;
+ return l;
+}
- assert(c);
- assert(pattern);
- assert(e);
- assert(cbdata);
+static AvahiSRBLookup *lookup_find(
+ AvahiSRecordBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiLookupFlags flags,
+ AvahiKey *key) {
+
+ AvahiSRBLookup *l;
+
+ assert(b);
- if (cbdata->record_browser->dead)
- return NULL;
+ for (l = b->lookups; l; l = l->lookups_next) {
- cbdata->record_browser->callback(
- cbdata->record_browser,
- cbdata->interface->hardware->index,
- cbdata->interface->protocol,
- AVAHI_BROWSER_NEW,
- e->record,
- cbdata->record_browser->userdata);
+ 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 scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
- AvahiSRecordBrowser *b = userdata;
- struct cbdata cbdata;
+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);
+
+ 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_NOT_FOUND:
+ 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 */
+ assert(avahi_key_equal(b->key, l->key));
+
+ 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_NOT_FOUND:
+ case AVAHI_BROWSER_FAILURE:
+ /* Not defined for multicast DNS */
+ abort();
- cbdata.record_browser = b;
- cbdata.interface = i;
+ }
+
+ 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) {
- assert(m);
- assert(i);
+ 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 (!b->dead)
- avahi_cache_walk(i->cache, b->key, scan_cache_callback, &cbdata);
+ 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 void scan_callback(AvahiTimeEvent *e, void *userdata) {
+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);
+}
+
+static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) {
+ AvahiKey *k;
+ AvahiSRBLookup *n;
+ 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(AvahiTimeEvent *e, void *userdata) {
AvahiSRecordBrowser *b = userdata;
+ int n;
+
assert(b);
+ assert(!b->dead);
- /* Scan the caches */
- if (!b->dead)
- avahi_interface_monitor_walk(b->server->monitor, b->interface, b->protocol, scan_interface_callback, b);
+ /* 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->scan_time_event) {
- avahi_time_event_free(b->scan_time_event);
- b->scan_time_event = NULL;
+ if (b->dead)
+ return;
+
+ if (n < 0) {
+ /* sending of the initial query failed */
+
+ b->callback(
+ b, b->interface, b->protocol, AVAHI_BROWSER_FAILURE, NULL,
+ b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_CALLBACK_WIDE_AREA : AVAHI_LOOKUP_CALLBACK_MULTICAST,
+ b->userdata);
+
+ avahi_s_record_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_CALLBACK_WIDE_AREA : AVAHI_LOOKUP_CALLBACK_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_CALLBACK_WIDE_AREA, b->userdata);
}
}
void avahi_s_record_browser_restart(AvahiSRecordBrowser *b) {
assert(b);
+ assert(!b->dead);
- if (!b->scan_time_event) {
- b->scan_time_event = avahi_time_event_new(b->server->time_event_queue, NULL, scan_callback, b);
- assert(b->scan_time_event);
+ avahi_s_record_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);
}
+}
- avahi_server_post_query(b->server, b->interface, b->protocol, b->key);
+#define CHECK_VALIDITY_RETURN_NULL(server, expression, error) { \
+ if (!(expression)) { \
+ avahi_server_set_errno((server), (error)); \
+ return NULL; \
+ } \
}
-AvahiSRecordBrowser *avahi_s_record_browser_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key, AvahiSRecordBrowserCallback callback, void* userdata) {
- AvahiSRecordBrowser *b, *t;
- struct timeval tv;
+AvahiSRecordBrowser *avahi_s_record_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiKey *key,
+ AvahiLookupFlags flags,
+ AvahiSRecordBrowserCallback callback,
+ void* userdata) {
+
+ AvahiSRecordBrowser *b;
assert(server);
assert(key);
assert(callback);
- if (avahi_key_is_pattern(key)) {
- avahi_server_set_errno(server, AVAHI_ERR_IS_PATTERN);
- return NULL;
- }
-
- if (!avahi_key_is_valid(key)) {
- avahi_server_set_errno(server, AVAHI_ERR_INVALID_KEY);
- return NULL;
- }
+ CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ CHECK_VALIDITY_RETURN_NULL(server, !avahi_key_is_pattern(key), AVAHI_ERR_IS_PATTERN);
+ CHECK_VALIDITY_RETURN_NULL(server, avahi_key_is_valid(key), AVAHI_ERR_INVALID_KEY);
+ CHECK_VALIDITY_RETURN_NULL(server, AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+ 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);
@@ -162,31 +537,43 @@ AvahiSRecordBrowser *avahi_s_record_browser_new(AvahiServer *server, AvahiIfInde
b->dead = 0;
b->server = server;
- b->key = avahi_key_ref(key);
b->interface = interface;
b->protocol = protocol;
+ b->key = avahi_key_ref(key);
+ b->flags = flags;
b->callback = callback;
b->userdata = userdata;
- b->sec_delay = 1;
- avahi_server_post_query(b->server, b->interface, b->protocol, b->key);
+ b->n_lookups = 0;
+ AVAHI_LLIST_HEAD_INIT(AvahiSRBLookup, b->lookups);
+ b->root_lookup = NULL;
- avahi_elapse_time(&tv, b->sec_delay*1000, 0);
- b->query_time_event = avahi_time_event_new(server->time_event_queue, &tv, elapse_callback, b);
-
AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, browser, server->record_browsers, b);
- /* Add the new entry to the record_browser hash table */
- t = avahi_hashmap_lookup(server->record_browser_hashmap, key);
- AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, by_key, t, b);
- avahi_hashmap_replace(server->record_browser_hashmap, key, t);
-
- /* The currenlty cached entries are scanned a bit later */
- b->scan_time_event = avahi_time_event_new(server->time_event_queue, NULL, scan_callback, b);
- assert(b->scan_time_event);
+ /* The currently cached entries are scanned a bit later, and than we will start querying, too */
+ b->defer_time_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b);
+ assert(b->defer_time_event);
+
return b;
}
+void avahi_s_record_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;
+ }
+
+
+
+}
+
void avahi_s_record_browser_free(AvahiSRecordBrowser *b) {
assert(b);
assert(!b->dead);
@@ -194,36 +581,16 @@ void avahi_s_record_browser_free(AvahiSRecordBrowser *b) {
b->dead = 1;
b->server->need_browser_cleanup = 1;
- if (b->query_time_event) {
- avahi_time_event_free(b->query_time_event);
- b->query_time_event = NULL;
- }
-
- if (b->scan_time_event) {
- avahi_time_event_free(b->scan_time_event);
- b->scan_time_event = NULL;
- }
+ avahi_s_record_browser_cancel(b);
}
void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b) {
- AvahiSRecordBrowser *t;
-
assert(b);
+
+ avahi_s_record_browser_cancel(b);
AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, browser, b->server->record_browsers, b);
- t = avahi_hashmap_lookup(b->server->record_browser_hashmap, b->key);
- AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, by_key, t, b);
- if (t)
- avahi_hashmap_replace(b->server->record_browser_hashmap, t->key, t);
- else
- avahi_hashmap_remove(b->server->record_browser_hashmap, b->key);
-
- if (b->query_time_event)
- avahi_time_event_free(b->query_time_event);
- if (b->scan_time_event)
- avahi_time_event_free(b->scan_time_event);
-
avahi_key_unref(b->key);
avahi_free(b);
@@ -235,46 +602,19 @@ void avahi_browser_cleanup(AvahiServer *server) {
assert(server);
- for (b = server->record_browsers; b; b = n) {
- n = b->browser_next;
+ while (server->need_browser_cleanup) {
+ server->need_browser_cleanup = 0;
- if (b->dead)
- avahi_s_record_browser_destroy(b);
- }
-
- server->need_browser_cleanup = 0;
-}
-
-void avahi_browser_notify(AvahiServer *server, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event) {
- AvahiSRecordBrowser *b;
-
- assert(server);
- assert(record);
-
- for (b = avahi_hashmap_lookup(server->record_browser_hashmap, record->key); b; b = b->by_key_next)
- if (!b->dead && avahi_interface_match(i, b->interface, b->protocol))
- b->callback(b, i->hardware->index, i->protocol, event, record, b->userdata);
+ 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);
}
-int avahi_is_subscribed(AvahiServer *server, AvahiInterface *i, AvahiKey *k) {
- AvahiSRecordBrowser *b;
- assert(server);
- assert(k);
-
- for (b = avahi_hashmap_lookup(server->record_browser_hashmap, k); b; b = b->by_key_next)
- if (!b->dead && avahi_interface_match(i, b->interface, b->protocol))
- return 1;
-
- return 0;
-}
-
-void avahi_browser_new_interface(AvahiServer*s, AvahiInterface *i) {
- AvahiSRecordBrowser *b;
-
- assert(s);
- assert(i);
-
- for (b = s->record_browsers; b; b = b->browser_next)
- if (avahi_interface_match(i, b->interface, b->protocol))
- avahi_interface_post_query(i, b->key, 0);
-}
diff --git a/avahi-core/browse.h b/avahi-core/browse.h
index 0d1cdb8..70e724f 100644
--- a/avahi-core/browse.h
+++ b/avahi-core/browse.h
@@ -23,19 +23,43 @@
***/
#include <avahi-common/llist.h>
+
#include "core.h"
#include "timeeventq.h"
#include "server.h"
+#include "dns.h"
+#include "lookup.h"
-void avahi_browser_cleanup(AvahiServer *server);
-void avahi_browser_notify(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event);
+typedef struct AvahiSRBLookup AvahiSRBLookup;
-int avahi_is_subscribed(AvahiServer *s, AvahiInterface *i, AvahiKey *k);
+struct AvahiSRecordBrowser {
+ AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, browser);
+ int dead;
+ AvahiServer *server;
-void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b);
+ AvahiKey *key;
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ AvahiLookupFlags flags;
+
+ AvahiTimeEvent *defer_time_event;
+
+ AvahiSRecordBrowserCallback callback;
+ void* userdata;
+
+ /* Lookup data */
+ AVAHI_LLIST_HEAD(AvahiSRBLookup, lookups);
+ unsigned n_lookups;
+ AvahiSRBLookup *root_lookup;
+};
+
+void avahi_browser_cleanup(AvahiServer *server);
+
+void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b);
void avahi_s_record_browser_restart(AvahiSRecordBrowser *b);
+void avahi_s_record_browser_cancel(AvahiSRecordBrowser *b);
-void avahi_browser_new_interface(AvahiServer*s, AvahiInterface *i);
+#define AVAHI_VALID_FLAGS(flags, max) (!((flags) & ~(max)))
#endif
diff --git a/avahi-core/cache.c b/avahi-core/cache.c
index 924b8c9..6332b12 100644
--- a/avahi-core/cache.c
+++ b/avahi-core/cache.c
@@ -32,7 +32,7 @@
#include "cache.h"
#include "log.h"
-#define AVAHI_MAX_CACHE_ENTRIES 200
+#define AVAHI_MAX_CACHE_ENTRIES 500
static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) {
AvahiCacheEntry *t;
@@ -56,8 +56,8 @@ static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) {
if (e->time_event)
avahi_time_event_free(e->time_event);
- avahi_browser_notify(c->server, c->interface, e->record, AVAHI_BROWSER_REMOVE);
-
+ avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_REMOVE);
+
avahi_record_unref(e->record);
avahi_free(e);
@@ -204,7 +204,7 @@ static void elapse_func(AvahiTimeEvent *t, void *userdata) {
assert(percent > 0);
/* Request a cache update, if we are subscribed to this entry */
- if (avahi_is_subscribed(e->cache->server, e->cache->interface, e->record->key)) {
+ if (avahi_querier_exists(e->cache->interface, e->record->key)) {
/* avahi_log_debug("Requesting cache entry update at %i%% for %s.", percent, txt); */
avahi_interface_post_query(e->cache->interface, e->record->key, 1);
}
@@ -345,7 +345,7 @@ void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const Av
c->n_entries++;
/* Notify subscribers */
- avahi_browser_notify(c->server, c->interface, e->record, AVAHI_BROWSER_NEW);
+ avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_NEW);
}
e->origin = *a;
diff --git a/avahi-core/conformance-test.c b/avahi-core/conformance-test.c
index 3702372..392dddc 100644
--- a/avahi-core/conformance-test.c
+++ b/avahi-core/conformance-test.c
@@ -37,6 +37,7 @@
#include "core.h"
#include "log.h"
+#include "lookup.h"
static char *name = NULL;
static AvahiSEntryGroup *group = NULL;
@@ -73,7 +74,7 @@ static void create_service(const char *t) {
else
group = avahi_s_entry_group_new(avahi, entry_group_callback, NULL);
- avahi_server_add_service(avahi, group, 0, AF_UNSPEC, name, "_http._tcp", NULL, NULL, 80, "foo", NULL);
+ avahi_server_add_service(avahi, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, name, "_http._tcp", NULL, NULL, 80, "foo", NULL);
avahi_s_entry_group_commit(group);
try++;
diff --git a/avahi-core/core.h b/avahi-core/core.h
index abc30c0..705fa31 100644
--- a/avahi-core/core.h
+++ b/avahi-core/core.h
@@ -22,14 +22,8 @@
USA.
***/
-/** \file core.h The Avahi Multicast DNS and DNS Service Discovery implmentation. */
+/** \file core.h The Avahi Multicast DNS and DNS Service Discovery implementation. */
-/** \example core-publish-service.c Example how to register a DNS-SD
- * service using an embedded mDNS stack. It behaves like a network
- * printer registering both an IPP and a BSD LPR service. */
-
-/** \example core-browse-services.c Example how to browse for DNS-SD
- * services using an embedded mDNS stack. */
#include <avahi-common/cdecl.h>
@@ -40,9 +34,6 @@ AVAHI_C_DECL_BEGIN
/** An mDNS responder object */
typedef struct AvahiServer AvahiServer;
-/** A group of locally registered DNS RRs */
-typedef struct AvahiSEntryGroup AvahiSEntryGroup;
-
#ifndef DOXYGEN_SHOULD_SKIP_THIS
AVAHI_C_DECL_END
#endif
@@ -56,6 +47,9 @@ AVAHI_C_DECL_END
AVAHI_C_DECL_BEGIN
#endif
+/** Maximum number of defined DNS servers for wide area DNS */
+#define AVAHI_MAX_WIDE_AREA_SERVERS 4
+
/** Flags for server entries */
typedef enum {
AVAHI_ENTRY_NULL = 0, /**< No special flags */
@@ -68,13 +62,10 @@ typedef enum {
/** Prototype for callback functions which are called whenever the state of an AvahiServer object changes */
typedef void (*AvahiServerCallback) (AvahiServer *s, AvahiServerState state, void* userdata);
-/** Prototype for callback functions which are called whenever the state of an AvahiSEntryGroup object changes */
-typedef void (*AvahiSEntryGroupCallback) (AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata);
-
/** Stores configuration options for a server instance */
typedef struct AvahiServerConfig {
- char *host_name; /**< Default host name. If left empty defaults to the result of gethostname(2) of the libc */
- char *domain_name; /**< Default domain name. If left empty defaults to .local */
+ char *host_name; /**< Default host name. If left empty defaults to the result of gethostname(2) of the libc */
+ char *domain_name; /**< Default domain name. If left empty defaults to .local */
int use_ipv4; /**< Enable IPv4 support */
int use_ipv6; /**< Enable IPv6 support */
int publish_hinfo; /**< Register a HINFO record for the host containing the local OS and CPU type */
@@ -86,6 +77,9 @@ typedef struct AvahiServerConfig {
int enable_reflector; /**< Reflect incoming mDNS traffic to all local networks. This allows mDNS based network browsing beyond ethernet borders */
int reflect_ipv; /**< if enable_reflector is 1, enable/disable reflecting between IPv4 and IPv6 */
int add_service_cookie; /**< Add magic service cookie to all locally generated records implicitly */
+ int enable_wide_area; /**< Enable wide area support */
+ AvahiAddress wide_area_servers[AVAHI_MAX_WIDE_AREA_SERVERS]; /** Unicast DNS server to use for wide area lookup */
+ unsigned n_wide_area_servers; /**< Number of servers in wide_area_servers[] */
} AvahiServerConfig;
/** Allocate a new mDNS responder object. */
@@ -156,412 +150,12 @@ void avahi_server_set_data(AvahiServer *s, void* userdata);
/** Return the current state of the server object */
AvahiServerState avahi_server_get_state(AvahiServer *s);
-/** Iterate through all local entries of the server. (when g is NULL)
- * or of a specified entry group. At the first call state should point
- * to a NULL initialized void pointer, That pointer is used to track
- * the current iteration. It is not safe to call any other
- * avahi_server_xxx() function during the iteration. If the last entry
- * has been read, NULL is returned. */
-const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state);
-
/** Callback prototype for avahi_server_dump() */
typedef void (*AvahiDumpCallback)(const char *text, void* userdata);
/** Dump the current server status by calling "callback" for each line. */
int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata);
-/** Create a new entry group. The specified callback function is
- * called whenever the state of the group changes. Use entry group
- * objects to keep track of you RRs. Add new RRs to a group using
- * avahi_server_add_xxx(). Make sure to call avahi_s_entry_group_commit()
- * to start the registration process for your RRs */
-AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata);
-
-/** Free an entry group. All RRs assigned to the group are removed from the server */
-void avahi_s_entry_group_free(AvahiSEntryGroup *g);
-
-/** Commit an entry group. This starts the probing and registration process for all RRs in the group */
-int avahi_s_entry_group_commit(AvahiSEntryGroup *g);
-
-/** Remove all entries from the entry group and reset the state to AVAHI_ENTRY_GROUP_UNCOMMITED. */
-void avahi_s_entry_group_reset(AvahiSEntryGroup *g);
-
-/** Return 1 if the entry group is empty, i.e. has no records attached. */
-int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g);
-
-/** Return the current state of the specified entry group */
-AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g);
-
-/** Change the opaque user data pointer attached to an entry group object */
-void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata);
-
-/** Return the opaque user data pointer currently set for the entry group object */
-void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g);
-
-/** Add a new resource record to the server. Returns 0 on success, negative otherwise. */
-int avahi_server_add(
- AvahiServer *s, /**< The server object to add this record to */
- AvahiSEntryGroup *g, /**< An entry group object if this new record shall be attached to one, or NULL. If you plan to remove the record sometime later you a required to pass an entry group object here. */
- AvahiIfIndex interface, /**< A numeric index of a network interface to attach this record to, or AVAHI_IF_UNSPEC to attach this record to all interfaces */
- AvahiProtocol protocol, /**< A protocol family to attach this record to. One of the AVAHI_PROTO_xxx constants. Use AVAHI_PROTO_UNSPEC to make this record available on all protocols (wich means on both IPv4 and IPv6). */
- AvahiEntryFlags flags, /**< Special flags for this record */
- AvahiRecord *r /**< The record to add. This function increases the reference counter of this object. */ );
-
-/** Add a PTR RR to the server. See avahi_server_add() for more information. */
-int avahi_server_add_ptr(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiEntryFlags flags,
- uint32_t ttl, /**< DNS TTL for this record */
- const char *name, /**< PTR record name */
- const char *dest /**< pointer destination */ );
-
-/** Add a PTR RR to the server. See avahi_server_add() for more information. */
-int avahi_server_add_txt(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiEntryFlags flags,
- uint32_t ttl, /**< DNS TTL for this record */
- const char *name, /**< TXT record name */
- ... /**< Text record data, terminated by NULL */) AVAHI_GCC_SENTINEL;
-
-/** Add a PTR RR to the server. Mostly identical to
- * avahi_server_add_text but takes a va_list instead of a variable
- * number of arguments */
-int avahi_server_add_txt_va(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiEntryFlags flags,
- uint32_t ttl,
- const char *name,
- va_list va);
-
-/** Add a PTR RR to the server. Mostly identical to
- * avahi_server_add_text but takes an AvahiStringList record instead of a variable
- * number of arguments. */
-int avahi_server_add_txt_strlst(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiEntryFlags flags,
- uint32_t ttl,
- const char *name,
- AvahiStringList *strlst /**< TXT decord data as a AvahiString. This routine makes a deep copy of this object. */ );
-
-/** Add an IP address mapping to the server. This will add both the
- * host-name-to-address and the reverse mapping to the server. See
- * avahi_server_add() for more information. If adding one of the RRs
- * fails, the function returns with an error, but it is not defined if
- * the other RR is deleted from the server or not. Therefore, you have
- * to free the AvahiSEntryGroup and create a new one before
- * proceeding. */
-int avahi_server_add_address(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiEntryFlags flags,
- const char *name,
- AvahiAddress *a);
-
-/** Add an DNS-SD service to the Server. This will add all required
- * RRs to the server. See avahi_server_add() for more information. If
- * adding one of the RRs fails, the function returns with an error,
- * but it is not defined if the other RR is deleted from the server or
- * not. Therefore, you have to free the AvahiSEntryGroup and create a
- * new one before proceeding. */
-int avahi_server_add_service(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *name, /**< Service name, e.g. "Lennart's Files" */
- const char *type, /**< DNS-SD type, e.g. "_http._tcp" */
- const char *domain,
- const char *host, /**< Host name where this servcie resides, or NULL if on the local host */
- uint16_t port, /**< Port number of the service */
- ... /**< Text records, terminated by NULL */) AVAHI_GCC_SENTINEL;
-
-/** Mostly identical to avahi_server_add_service(), but takes an va_list for the TXT records. */
-int avahi_server_add_service_va(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *name,
- const char *type,
- const char *domain,
- const char *host,
- uint16_t port,
- va_list va);
-
-/** Mostly identical to avahi_server_add_service(), but takes an AvahiStringList object for the TXT records. The AvahiStringList object is copied. */
-int avahi_server_add_service_strlst(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *name,
- const char *type,
- const char *domain,
- const char *host,
- uint16_t port,
- AvahiStringList *strlst);
-
-/** The type of DNS server */
-typedef enum {
- AVAHI_DNS_SERVER_RESOLVE, /**< Unicast DNS servers for normal resolves (_domain._udp)*/
- AVAHI_DNS_SERVER_UPDATE /**< Unicast DNS servers for updates (_dns-update._udp)*/
-} AvahiDNSServerType;
-
-/** Publish the specified unicast DNS server address via mDNS. You may
- * browse for records create this way wit
- * avahi_s_dns_server_browser_new(). */
-int avahi_server_add_dns_server_address(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *domain,
- AvahiDNSServerType type,
- const AvahiAddress *address,
- uint16_t port /** should be 53 */);
-
-/** Similar to avahi_server_add_dns_server_address(), but specify a
-host name instead of an address. The specified host name should be
-resolvable via mDNS */
-int avahi_server_add_dns_server_name(
- AvahiServer *s,
- AvahiSEntryGroup *g,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *domain,
- AvahiDNSServerType type,
- const char *name,
- uint16_t port /** should be 53 */);
-
-/** A browsing object for arbitrary RRs */
-typedef struct AvahiSRecordBrowser AvahiSRecordBrowser;
-
-/** Callback prototype for AvahiSRecordBrowser events */
-typedef void (*AvahiSRecordBrowserCallback)(
- AvahiSRecordBrowser *b, /**< The AvahiSRecordBrowser object that is emitting this callback */
- AvahiIfIndex interface, /**< Logical OS network interface number the record was found on */
- AvahiProtocol protocol, /**< Protocol number the record was found. */
- AvahiBrowserEvent event, /**< Browsing event, either AVAHI_BROWSER_NEW or AVAHI_BROWSER_REMOVE */
- AvahiRecord *record, /**< The record that was found */
- void* userdata /**< Arbitrary user data passed to avahi_s_record_browser_new() */ );
-
-/** Create a new browsing object for arbitrary RRs */
-AvahiSRecordBrowser *avahi_s_record_browser_new(
- AvahiServer *server, /**< The server object to which attach this query */
- AvahiIfIndex interface, /**< Logical OS interface number where to look for the records, or AVAHI_IF_UNSPEC to look on interfaces */
- AvahiProtocol protocol, /**< Protocol number to use when looking for the record, or AVAHI_PROTO_UNSPEC to look on all protocols */
- AvahiKey *key, /**< The search key */
- AvahiSRecordBrowserCallback callback, /**< The callback to call on browsing events */
- void* userdata /**< Arbitrary use suppliable data which is passed to the callback */);
-
-/** Free an AvahiSRecordBrowser object */
-void avahi_s_record_browser_free(AvahiSRecordBrowser *b);
-
-/** A host name to IP adddress resolver object */
-typedef struct AvahiSHostNameResolver AvahiSHostNameResolver;
-
-/** Callback prototype for AvahiSHostNameResolver events */
-typedef void (*AvahiSHostNameResolverCallback)(
- AvahiSHostNameResolver *r,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiResolverEvent event, /**< Resolving event */
- const char *host_name, /**< Host name which should be resolved. May differ in case from the query */
- const AvahiAddress *a, /**< The address, or NULL if the host name couldn't be resolved. */
- void* userdata);
-
-/** Create an AvahiSHostNameResolver object for resolving a host name to an adddress. See AvahiSRecordBrowser for more info on the paramters. */
-AvahiSHostNameResolver *avahi_s_host_name_resolver_new(
- AvahiServer *server,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *host_name, /**< The host name to look for */
- AvahiProtocol aprotocol, /**< The address family of the desired address or AVAHI_PROTO_UNSPEC if doesn't matter. */
- AvahiSHostNameResolverCallback calback,
- void* userdata);
-
-/** Free a AvahiSHostNameResolver object */
-void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r);
-
-/** An IP address to host name resolver object ("reverse lookup") */
-typedef struct AvahiSAddressResolver AvahiSAddressResolver;
-
-/** Callback prototype for AvahiSAddressResolver events */
-typedef void (*AvahiSAddressResolverCallback)(
- AvahiSAddressResolver *r,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiResolverEvent event,
- const AvahiAddress *a,
- const char *host_name, /**< A host name for the specified address, if one was found, i.e. event == AVAHI_RESOLVER_FOUND */
- void* userdata);
-
-/** Create an AvahiSAddressResolver object. See AvahiSRecordBrowser for more info on the paramters. */
-AvahiSAddressResolver *avahi_s_address_resolver_new(
- AvahiServer *server,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const AvahiAddress *address,
- AvahiSAddressResolverCallback calback,
- void* userdata);
-
-/** Free an AvahiSAddressResolver object */
-void avahi_s_address_resolver_free(AvahiSAddressResolver *r);
-
-/** A local domain browsing object. May be used to enumerate domains used on the local LAN */
-typedef struct AvahiSDomainBrowser AvahiSDomainBrowser;
-
-/** Callback prototype for AvahiSDomainBrowser events */
-typedef void (*AvahiSDomainBrowserCallback)(
- AvahiSDomainBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *domain,
- void* userdata);
-
-/** Create a new AvahiSDomainBrowser object */
-AvahiSDomainBrowser *avahi_s_domain_browser_new(
- AvahiServer *server,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *domain,
- AvahiDomainBrowserType type,
- AvahiSDomainBrowserCallback callback,
- void* userdata);
-
-/** Free an AvahiSDomainBrowser object */
-void avahi_s_domain_browser_free(AvahiSDomainBrowser *b);
-
-/** A DNS-SD service type browsing object. May be used to enumerate the service types of all available services on the local LAN */
-typedef struct AvahiSServiceTypeBrowser AvahiSServiceTypeBrowser;
-
-/** Callback prototype for AvahiSServiceTypeBrowser events */
-typedef void (*AvahiSServiceTypeBrowserCallback)(
- AvahiSServiceTypeBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *type,
- const char *domain,
- void* userdata);
-
-/** Create a new AvahiSServiceTypeBrowser object. */
-AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(
- AvahiServer *server,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *domain,
- AvahiSServiceTypeBrowserCallback callback,
- void* userdata);
-
-/** Free an AvahiSServiceTypeBrowser object */
-void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b);
-
-/** A DNS-SD service browser. Use this to enumerate available services of a certain kind on the local LAN. Use AvahiSServiceResolver to get specific service data like address and port for a service. */
-typedef struct AvahiSServiceBrowser AvahiSServiceBrowser;
-
-/** Callback prototype for AvahiSServiceBrowser events */
-typedef void (*AvahiSServiceBrowserCallback)(
- AvahiSServiceBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *name /**< Service name, e.g. "Lennart's Files" */,
- const char *type /**< DNS-SD type, e.g. "_http._tcp" */,
- const char *domain /**< Domain of this service, e.g. "local" */,
- void* userdata);
-
-/** Create a new AvahiSServiceBrowser object. */
-AvahiSServiceBrowser *avahi_s_service_browser_new(
- AvahiServer *server,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *service_type /** DNS-SD service type, e.g. "_http._tcp" */,
- const char *domain,
- AvahiSServiceBrowserCallback callback,
- void* userdata);
-
-/** Free an AvahiSServiceBrowser object */
-void avahi_s_service_browser_free(AvahiSServiceBrowser *b);
-
-/** A DNS-SD service resolver. Use this to retrieve addres, port and TXT data for a DNS-SD service */
-typedef struct AvahiSServiceResolver AvahiSServiceResolver;
-
-/** Callback prototype for AvahiSServiceResolver events */
-typedef void (*AvahiSServiceResolverCallback)(
- AvahiSServiceResolver *r,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiResolverEvent event, /**< Is AVAHI_RESOLVER_FOUND when the service was resolved successfully, and everytime it changes. Is AVAHI_RESOLVER_TIMOUT when the service failed to resolve or disappeared. */
- const char *name, /**< Service name */
- const char *type, /**< Service Type */
- const char *domain,
- const char *host_name, /**< Host name of the service */
- const AvahiAddress *a, /**< The resolved host name */
- uint16_t port, /**< Service name */
- AvahiStringList *txt, /**< TXT record data */
- void* userdata);
-
-/** Create a new AvahiSServiceResolver object. The specified callback function will be called with the resolved service data. */
-AvahiSServiceResolver *avahi_s_service_resolver_new(
- AvahiServer *server,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *name,
- const char *type,
- const char *domain,
- AvahiProtocol aprotocol, /**< Address family of the desired service address. Use AVAHI_PROTO_UNSPEC if you don't care */
- AvahiSServiceResolverCallback calback,
- void* userdata);
-
-/** Free an AvahiSServiceResolver object */
-void avahi_s_service_resolver_free(AvahiSServiceResolver *r);
-
-/** A domain service browser object. Use this to browse for
- * conventional unicast DNS servers which may be used to resolve
- * conventional domain names */
-typedef struct AvahiSDNSServerBrowser AvahiSDNSServerBrowser;
-
-/** Callback prototype for AvahiSDNSServerBrowser events */
-typedef void (*AvahiSDNSServerBrowserCallback)(
- AvahiSDNSServerBrowser *b,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- AvahiBrowserEvent event,
- const char *host_name, /**< Host name of the DNS server, probably useless */
- const AvahiAddress *a, /**< Address of the DNS server */
- uint16_t port, /**< Port number of the DNS servers, probably 53 */
- void* userdata);
-
-/** Create a new AvahiSDNSServerBrowser object */
-AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(
- AvahiServer *server,
- AvahiIfIndex interface,
- AvahiProtocol protocol,
- const char *domain,
- AvahiDNSServerType type,
- AvahiProtocol aprotocol, /**< Address protocol for the DNS server */
- AvahiSDNSServerBrowserCallback callback,
- void* userdata);
-
-/** Free an AvahiSDNSServerBrowser object */
-void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b);
-
/** Return the last error code */
int avahi_server_errno(AvahiServer *s);
@@ -571,6 +165,9 @@ uint32_t avahi_server_get_local_service_cookie(AvahiServer *s);
/** Return 1 if there is a local service with the specified credentials registeresd. Return 0 if not, negative on failure */
int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex, AvahiProtocol protocol, const char *name, const char *type, const char*domain);
+/** Set the wide area DNS servers */
+int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n);
+
#ifndef DOXYGEN_SHOULD_SKIP_THIS
AVAHI_C_DECL_END
#endif
diff --git a/avahi-core/dns.c b/avahi-core/dns.c
index 9bc7aec..9412470 100644
--- a/avahi-core/dns.c
+++ b/avahi-core/dns.c
@@ -286,21 +286,37 @@ uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l) {
return d;
}
-int avahi_dns_packet_is_valid(AvahiDnsPacket *p) {
+int avahi_dns_packet_check_valid(AvahiDnsPacket *p) {
uint16_t flags;
assert(p);
- if (p->size < 12)
+ if (p->size < AVAHI_DNS_PACKET_HEADER_SIZE)
return -1;
flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS);
+
+ if (flags & AVAHI_DNS_FLAG_OPCODE)
+ return -1;
+
+ return 0;
+}
+
+int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p) {
+ uint16_t flags;
+ assert(p);
- if (flags & AVAHI_DNS_FLAG_OPCODE || flags & AVAHI_DNS_FLAG_RCODE)
+ if (avahi_dns_packet_check_valid(p) < 0)
+ return -1;
+
+ flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS);
+
+ if (flags & AVAHI_DNS_FLAG_RCODE)
return -1;
return 0;
}
+
int avahi_dns_packet_is_query(AvahiDnsPacket *p) {
assert(p);
@@ -478,7 +494,6 @@ AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_f
const void* start;
assert(p);
- assert(ret_cache_flush);
/* avahi_log_debug("consume_record()"); */
@@ -492,7 +507,8 @@ AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_f
/* avahi_log_debug("name = %s, rdlength = %u", name, rdlength); */
- *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH);
+ if (ret_cache_flush)
+ *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH);
class &= ~AVAHI_DNS_CACHE_FLUSH;
start = avahi_dns_packet_get_rptr(p);
@@ -608,16 +624,17 @@ AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_respo
uint16_t type, class;
assert(p);
- assert(ret_unicast_response);
if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
avahi_dns_packet_consume_uint16(p, &type) < 0 ||
avahi_dns_packet_consume_uint16(p, &class) < 0)
return NULL;
- *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE);
- class &= ~AVAHI_DNS_UNICAST_RESPONSE;
+ if (ret_unicast_response)
+ *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE);
+ class &= ~AVAHI_DNS_UNICAST_RESPONSE;
+
return avahi_key_new(name, class, type);
}
diff --git a/avahi-core/dns.h b/avahi-core/dns.h
index 4988994..743ad4e 100644
--- a/avahi-core/dns.h
+++ b/avahi-core/dns.h
@@ -58,7 +58,8 @@ uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int c
uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s);
int avahi_dns_packet_is_query(AvahiDnsPacket *p);
-int avahi_dns_packet_is_valid(AvahiDnsPacket *p);
+int avahi_dns_packet_check_valid(AvahiDnsPacket *p);
+int avahi_dns_packet_check_valid_multicast(AvahiDnsPacket *p);
int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v);
int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v);
@@ -97,8 +98,11 @@ size_t avahi_dns_packet_space(AvahiDnsPacket *p);
((uint16_t) !!ra << 7) | \
((uint16_t) !!ad << 5) | \
((uint16_t) !!cd << 4) | \
- ((uint16_t) (rd & 15)))
-
+ ((uint16_t) (rcode & 15)))
+
+#define AVAHI_MDNS_SUFFIX_LOCAL "local"
+#define AVAHI_MDNS_SUFFIX_ADDR_IPV4 "254.169.in-addr.arpa"
+#define AVAHI_MDNS_SUFFIX_ADDR_IPV6 "0.8.e.f.ip6.arpa"
#endif
diff --git a/avahi-core/hashmap.c b/avahi-core/hashmap.c
index 4d2fa3c..24a1249 100644
--- a/avahi-core/hashmap.c
+++ b/avahi-core/hashmap.c
@@ -57,7 +57,7 @@ struct AvahiHashmap {
static Entry* entry_get(AvahiHashmap *m, const void *key) {
unsigned idx;
Entry *e;
-
+
idx = m->hash_func(key) % HASH_MAP_SIZE;
for (e = m->entries[idx]; e; e = e->bucket_next)
@@ -91,7 +91,7 @@ AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_fu
assert(hash_func);
assert(equal_func);
- if (!(m = avahi_new(AvahiHashmap, 1)))
+ if (!(m = avahi_new0(AvahiHashmap, 1)))
return NULL;
m->hash_func = hash_func;
@@ -99,8 +99,6 @@ AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_fu
m->key_free_func = key_free_func;
m->value_free_func = value_free_func;
- memset(m->entries, 0, sizeof(m->entries));
-
AVAHI_LLIST_HEAD_INIT(Entry, m->entries_list);
return m;
@@ -231,6 +229,8 @@ unsigned avahi_string_hash(const void *data) {
const char *p = data;
unsigned hash = 0;
+ assert(p);
+
for (; *p; p++)
hash = 31 * hash + *p;
@@ -240,17 +240,25 @@ unsigned avahi_string_hash(const void *data) {
int avahi_string_equal(const void *a, const void *b) {
const char *p = a, *q = b;
+ assert(p);
+ assert(q);
+
return strcmp(p, q) == 0;
}
unsigned avahi_int_hash(const void *data) {
const int *i = data;
+ assert(i);
+
return (unsigned) *i;
}
int avahi_int_equal(const void *a, const void *b) {
const int *_a = a, *_b = b;
+ assert(_a);
+ assert(_b);
+
return *_a == *_b;
}
diff --git a/avahi-core/iface.c b/avahi-core/iface.c
index 1692938..f882988 100644
--- a/avahi-core/iface.c
+++ b/avahi-core/iface.c
@@ -42,6 +42,8 @@
#include "announce.h"
#include "util.h"
#include "log.h"
+#include "multicast-lookup.h"
+#include "querier.h"
static void update_address_rr(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a, int remove_rrs) {
assert(m);
@@ -180,11 +182,16 @@ static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, int send
assert(m);
assert(i);
+ /* Handle goodbyes and remove announcers */
avahi_goodbye_interface(m->server, i, send_goodbye);
avahi_response_scheduler_force(i->response_scheduler);
-
assert(!i->announcements);
+ /* Remove queriers */
+ avahi_querier_free_all(i);
+ avahi_hashmap_free(i->queriers_by_key);
+
+ /* Remove local RRs */
update_interface_rr(m, i, 1);
while (i->addresses)
@@ -271,6 +278,9 @@ static void new_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiP
AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses);
AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, i->announcements);
+ AVAHI_LLIST_HEAD_INIT(AvahiQuerier, i->queriers);
+ i->queriers_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
+
i->cache = avahi_cache_new(m->server, i);
i->response_scheduler = avahi_response_scheduler_new(i);
i->query_scheduler = avahi_query_scheduler_new(i);
@@ -316,7 +326,7 @@ static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i
i->announcing = 1;
avahi_announce_interface(m->server, i);
- avahi_browser_new_interface(m->server, i);
+ avahi_multicast_lookup_engine_new_interface(m->server->multicast_lookup_engine, i);
} else if (!b && i->announcing) {
avahi_log_info("Interface %s.%s no longer relevant.", i->hardware->name, avahi_proto_to_string(i->protocol));
@@ -326,6 +336,8 @@ static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i
avahi_mdns_mcast_leave_ipv6(m->server->fd_ipv6, i->hardware->index);
avahi_goodbye_interface(m->server, i, 0);
+ avahi_querier_free_all(i);
+
avahi_response_scheduler_clear(i->response_scheduler);
avahi_query_scheduler_clear(i->query_scheduler);
avahi_probe_scheduler_clear(i->probe_scheduler);
@@ -585,7 +597,6 @@ void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) {
free_hw_interface(m, m->hw_interfaces, 1);
assert(!m->interfaces);
-
if (m->netlink)
avahi_netlink_free(m->netlink);
@@ -602,7 +613,7 @@ AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m,
AvahiInterface *i;
assert(m);
- assert(idx > 0);
+ assert(idx >= 0);
assert(protocol != AVAHI_PROTO_UNSPEC);
if (!(hw = avahi_interface_monitor_get_hw_interface(m, idx)))
@@ -757,7 +768,7 @@ int avahi_interface_address_relevant(AvahiInterfaceAddress *a) {
int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol) {
assert(i);
- if (idx > 0 && idx != i->hardware->index)
+ if (idx != AVAHI_IF_UNSPEC && idx != i->hardware->index)
return 0;
if (protocol != AVAHI_PROTO_UNSPEC && protocol != i->protocol)
@@ -770,7 +781,7 @@ void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex interfa
assert(m);
assert(callback);
- if (interface > 0) {
+ if (interface != AVAHI_IF_UNSPEC) {
if (protocol != AVAHI_PROTO_UNSPEC) {
AvahiInterface *i;
diff --git a/avahi-core/iface.h b/avahi-core/iface.h
index b78c76a..25eb324 100644
--- a/avahi-core/iface.h
+++ b/avahi-core/iface.h
@@ -38,6 +38,8 @@ typedef struct AvahiHwInterface AvahiHwInterface;
#include "probe-sched.h"
#include "dns.h"
#include "announce.h"
+#include "browse.h"
+#include "querier.h"
#define AVAHI_MAX_MAC_ADDRESS 32
@@ -85,12 +87,16 @@ struct AvahiInterface {
int announcing;
AvahiCache *cache;
+
AvahiQueryScheduler *query_scheduler;
AvahiResponseScheduler * response_scheduler;
AvahiProbeScheduler *probe_scheduler;
AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses);
AVAHI_LLIST_HEAD(AvahiAnnouncement, announcements);
+
+ AvahiHashmap *queriers_by_key;
+ AVAHI_LLIST_HEAD(AvahiQuerier, queriers);
};
struct AvahiInterfaceAddress {
diff --git a/avahi-core/lookup.h b/avahi-core/lookup.h
new file mode 100644
index 0000000..518a4c9
--- /dev/null
+++ b/avahi-core/lookup.h
@@ -0,0 +1,284 @@
+#ifndef foolookuphfoo
+#define foolookuphfoo
+
+/* $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
+ USA.
+***/
+
+/** \file lookup.h Functions for browsing/resolving services and other RRs */
+
+/** \example core-browse-services.c Example how to browse for DNS-SD
+ * services using an embedded mDNS stack. */
+
+#include <avahi-common/cdecl.h>
+#include <avahi-common/defs.h>
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+AVAHI_C_DECL_BEGIN
+#endif
+
+/** A browsing object for arbitrary RRs */
+typedef struct AvahiSRecordBrowser AvahiSRecordBrowser;
+
+/** A host name to IP adddress resolver object */
+typedef struct AvahiSHostNameResolver AvahiSHostNameResolver;
+
+/** An IP address to host name resolver object ("reverse lookup") */
+typedef struct AvahiSAddressResolver AvahiSAddressResolver;
+
+/** A local domain browsing object. May be used to enumerate domains used on the local LAN */
+typedef struct AvahiSDomainBrowser AvahiSDomainBrowser;
+
+/** A DNS-SD service type browsing object. May be used to enumerate the service types of all available services on the local LAN */
+typedef struct AvahiSServiceTypeBrowser AvahiSServiceTypeBrowser;
+
+/** A DNS-SD service browser. Use this to enumerate available services of a certain kind on the local LAN. Use AvahiSServiceResolver to get specific service data like address and port for a service. */
+typedef struct AvahiSServiceBrowser AvahiSServiceBrowser;
+
+/** A DNS-SD service resolver. Use this to retrieve addres, port and TXT data for a DNS-SD service */
+typedef struct AvahiSServiceResolver AvahiSServiceResolver;
+
+/** A domain service browser object. Use this to browse for
+ * conventional unicast DNS servers which may be used to resolve
+ * conventional domain names */
+typedef struct AvahiSDNSServerBrowser AvahiSDNSServerBrowser;
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+AVAHI_C_DECL_END
+#endif
+
+#include "core.h"
+#include "publish.h"
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+AVAHI_C_DECL_BEGIN
+#endif
+
+
+/** Callback prototype for AvahiSRecordBrowser events */
+typedef void (*AvahiSRecordBrowserCallback)(
+ AvahiSRecordBrowser *b, /**< The AvahiSRecordBrowser object that is emitting this callback */
+ AvahiIfIndex interface, /**< Logical OS network interface number the record was found on */
+ AvahiProtocol protocol, /**< Protocol number the record was found. */
+ AvahiBrowserEvent event, /**< Browsing event, either AVAHI_BROWSER_NEW or AVAHI_BROWSER_REMOVE */
+ AvahiRecord *record, /**< The record that was found */
+ AvahiLookupResultFlags flags, /**< Lookup flags */
+ void* userdata /**< Arbitrary user data passed to avahi_s_record_browser_new() */ );
+
+/** Create a new browsing object for arbitrary RRs */
+AvahiSRecordBrowser *avahi_s_record_browser_new(
+ AvahiServer *server, /**< The server object to which attach this query */
+ AvahiIfIndex interface, /**< Logical OS interface number where to look for the records, or AVAHI_IF_UNSPEC to look on interfaces */
+ AvahiProtocol protocol, /**< Protocol number to use when looking for the record, or AVAHI_PROTO_UNSPEC to look on all protocols */
+ AvahiKey *key, /**< The search key */
+ AvahiLookupFlags flags, /**< Lookup flags. Must have set either AVAHI_LOOKUP_FORCE_WIDE_AREA or AVAHI_LOOKUP_FORCE_MULTICAST, since domain based detection is not available here. */
+ AvahiSRecordBrowserCallback callback, /**< The callback to call on browsing events */
+ void* userdata /**< Arbitrary use suppliable data which is passed to the callback */);
+
+/** Free an AvahiSRecordBrowser object */
+void avahi_s_record_browser_free(AvahiSRecordBrowser *b);
+
+/** Callback prototype for AvahiSHostNameResolver events */
+typedef void (*AvahiSHostNameResolverCallback)(
+ AvahiSHostNameResolver *r,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event, /**< Resolving event */
+ const char *host_name, /**< Host name which should be resolved. May differ in case from the query */
+ const AvahiAddress *a, /**< The address, or NULL if the host name couldn't be resolved. */
+ AvahiLookupResultFlags flags, /**< Lookup flags */
+ void* userdata);
+
+/** Create an AvahiSHostNameResolver object for resolving a host name to an adddress. See AvahiSRecordBrowser for more info on the paramters. */
+AvahiSHostNameResolver *avahi_s_host_name_resolver_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *host_name, /**< The host name to look for */
+ AvahiProtocol aprotocol, /**< The address family of the desired address or AVAHI_PROTO_UNSPEC if doesn't matter. */
+ AvahiLookupFlags flags, /**< Lookup flags. */
+ AvahiSHostNameResolverCallback calback,
+ void* userdata);
+
+/** Free a AvahiSHostNameResolver object */
+void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r);
+
+/** Callback prototype for AvahiSAddressResolver events */
+typedef void (*AvahiSAddressResolverCallback)(
+ AvahiSAddressResolver *r,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const AvahiAddress *a,
+ const char *host_name, /**< A host name for the specified address, if one was found, i.e. event == AVAHI_RESOLVER_FOUND */
+ AvahiLookupResultFlags flags, /**< Lookup flags */
+ void* userdata);
+
+/** Create an AvahiSAddressResolver object. See AvahiSRecordBrowser for more info on the paramters. */
+AvahiSAddressResolver *avahi_s_address_resolver_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const AvahiAddress *address,
+ AvahiLookupFlags flags, /**< Lookup flags. */
+ AvahiSAddressResolverCallback calback,
+ void* userdata);
+
+/** Free an AvahiSAddressResolver object */
+void avahi_s_address_resolver_free(AvahiSAddressResolver *r);
+
+/** Callback prototype for AvahiSDomainBrowser events */
+typedef void (*AvahiSDomainBrowserCallback)(
+ AvahiSDomainBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *domain,
+ AvahiLookupResultFlags flags, /**< Lookup flags */
+ void* userdata);
+
+/** Create a new AvahiSDomainBrowser object */
+AvahiSDomainBrowser *avahi_s_domain_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *domain,
+ AvahiDomainBrowserType type,
+ AvahiLookupFlags flags, /**< Lookup flags. */
+ AvahiSDomainBrowserCallback callback,
+ void* userdata);
+
+/** Free an AvahiSDomainBrowser object */
+void avahi_s_domain_browser_free(AvahiSDomainBrowser *b);
+
+/** Callback prototype for AvahiSServiceTypeBrowser events */
+typedef void (*AvahiSServiceTypeBrowserCallback)(
+ AvahiSServiceTypeBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *type,
+ const char *domain,
+ AvahiLookupResultFlags flags, /**< Lookup flags */
+ void* userdata);
+
+/** Create a new AvahiSServiceTypeBrowser object. */
+AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *domain,
+ AvahiLookupFlags flags, /**< Lookup flags. */
+ AvahiSServiceTypeBrowserCallback callback,
+ void* userdata);
+
+/** Free an AvahiSServiceTypeBrowser object */
+void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b);
+
+/** Callback prototype for AvahiSServiceBrowser events */
+typedef void (*AvahiSServiceBrowserCallback)(
+ AvahiSServiceBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name /**< Service name, e.g. "Lennart's Files" */,
+ const char *type /**< DNS-SD type, e.g. "_http._tcp" */,
+ const char *domain /**< Domain of this service, e.g. "local" */,
+ AvahiLookupResultFlags flags, /**< Lookup flags */
+ void* userdata);
+
+/** Create a new AvahiSServiceBrowser object. */
+AvahiSServiceBrowser *avahi_s_service_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *service_type /** DNS-SD service type, e.g. "_http._tcp" */,
+ const char *domain,
+ AvahiLookupFlags flags, /**< Lookup flags. */
+ AvahiSServiceBrowserCallback callback,
+ void* userdata);
+
+/** Free an AvahiSServiceBrowser object */
+void avahi_s_service_browser_free(AvahiSServiceBrowser *b);
+
+/** Callback prototype for AvahiSServiceResolver events */
+typedef void (*AvahiSServiceResolverCallback)(
+ AvahiSServiceResolver *r,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event, /**< Is AVAHI_RESOLVER_FOUND when the service was resolved successfully, and everytime it changes. Is AVAHI_RESOLVER_TIMOUT when the service failed to resolve or disappeared. */
+ const char *name, /**< Service name */
+ const char *type, /**< Service Type */
+ const char *domain,
+ const char *host_name, /**< Host name of the service */
+ const AvahiAddress *a, /**< The resolved host name */
+ uint16_t port, /**< Service name */
+ AvahiStringList *txt, /**< TXT record data */
+ AvahiLookupResultFlags flags, /**< Lookup flags */
+ void* userdata);
+
+/** Create a new AvahiSServiceResolver object. The specified callback function will be called with the resolved service data. */
+AvahiSServiceResolver *avahi_s_service_resolver_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AvahiProtocol aprotocol, /**< Address family of the desired service address. Use AVAHI_PROTO_UNSPEC if you don't care */
+ AvahiLookupFlags flags, /**< Lookup flags. */
+ AvahiSServiceResolverCallback calback,
+ void* userdata);
+
+/** Free an AvahiSServiceResolver object */
+void avahi_s_service_resolver_free(AvahiSServiceResolver *r);
+
+/** Callback prototype for AvahiSDNSServerBrowser events */
+typedef void (*AvahiSDNSServerBrowserCallback)(
+ AvahiSDNSServerBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *host_name, /**< Host name of the DNS server, probably useless */
+ const AvahiAddress *a, /**< Address of the DNS server */
+ uint16_t port, /**< Port number of the DNS servers, probably 53 */
+ AvahiLookupResultFlags flags, /**< Lookup flags */
+ void* userdata);
+
+/** Create a new AvahiSDNSServerBrowser object */
+AvahiSDNSServerBrowser *avahi_s_dns_server_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *domain,
+ AvahiDNSServerType type,
+ AvahiProtocol aprotocol, /**< Address protocol for the DNS server */
+ AvahiLookupFlags flags, /**< Lookup flags. */
+ AvahiSDNSServerBrowserCallback callback,
+ void* userdata);
+
+/** Free an AvahiSDNSServerBrowser object */
+void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b);
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+AVAHI_C_DECL_END
+#endif
+
+#endif
diff --git a/avahi-core/multicast-lookup.c b/avahi-core/multicast-lookup.c
new file mode 100644
index 0000000..4f04ab8
--- /dev/null
+++ b/avahi-core/multicast-lookup.c
@@ -0,0 +1,346 @@
+/* $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
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <avahi-common/malloc.h>
+
+#include "server.h"
+#include "browse.h"
+#include "socket.h"
+#include "log.h"
+#include "hashmap.h"
+#include "multicast-lookup.h"
+
+struct AvahiMulticastLookup {
+ AvahiMulticastLookupEngine *engine;
+ int dead;
+
+ AvahiKey *key, *cname_key;
+
+ AvahiMulticastLookupCallback callback;
+ void *userdata;
+
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+
+ int queriers_added;
+
+ AvahiTimeEvent *all_for_now_event;
+
+ AVAHI_LLIST_FIELDS(AvahiMulticastLookup, lookups);
+ AVAHI_LLIST_FIELDS(AvahiMulticastLookup, by_key);
+};
+
+struct AvahiMulticastLookupEngine {
+ AvahiServer *server;
+
+ /* Lookups */
+ AVAHI_LLIST_HEAD(AvahiMulticastLookup, lookups);
+ AvahiHashmap *lookups_by_key;
+
+ int cleanup_dead;
+};
+
+static void all_for_now_callback(AvahiTimeEvent *e, void* userdata) {
+ AvahiMulticastLookup *l = userdata;
+
+ assert(e);
+ assert(l);
+
+ avahi_time_event_free(l->all_for_now_event);
+ l->all_for_now_event = NULL;
+
+ l->callback(l->engine, l->interface, l->protocol, AVAHI_BROWSER_ALL_FOR_NOW, AVAHI_LOOKUP_CALLBACK_MULTICAST, NULL, l->userdata);
+}
+
+AvahiMulticastLookup *avahi_multicast_lookup_new(
+ AvahiMulticastLookupEngine *e,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiKey *key,
+ AvahiMulticastLookupCallback callback,
+ void *userdata) {
+
+ AvahiMulticastLookup *l, *t;
+ struct timeval ctime;
+
+ assert(e);
+ assert(AVAHI_IF_VALID(interface));
+ assert(AVAHI_PROTO_VALID(protocol));
+ assert(key);
+ assert(callback);
+
+ l = avahi_new(AvahiMulticastLookup, 1);
+ l->engine = e;
+ l->dead = 0;
+ l->key = avahi_key_ref(key);
+ l->cname_key = avahi_key_new_cname(l->key);
+ l->callback = callback;
+ l->userdata = userdata;
+ l->interface = interface;
+ l->protocol = protocol;
+ l->all_for_now_event = NULL;
+ l->queriers_added = 0;
+
+ t = avahi_hashmap_lookup(e->lookups_by_key, l->key);
+ AVAHI_LLIST_PREPEND(AvahiMulticastLookup, by_key, t, l);
+ avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t);
+
+ AVAHI_LLIST_PREPEND(AvahiMulticastLookup, lookups, e->lookups, l);
+
+ avahi_querier_add_for_all(e->server, interface, protocol, l->key, &ctime);
+
+ /* add a second */
+ avahi_timeval_add(&ctime, 1000000);
+
+ l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &ctime, all_for_now_callback, l);
+
+ return l;
+}
+
+static void lookup_stop(AvahiMulticastLookup *l) {
+ assert(l);
+
+ l->callback = NULL;
+
+ if (l->queriers_added) {
+ avahi_querier_remove_for_all(l->engine->server, l->interface, l->protocol, l->key);
+ l->queriers_added = 0;
+ }
+
+ if (l->all_for_now_event) {
+ avahi_time_event_free(l->all_for_now_event);
+ l->all_for_now_event = NULL;
+ }
+}
+
+static void lookup_destroy(AvahiMulticastLookup *l) {
+ AvahiMulticastLookup *t;
+ assert(l);
+
+ lookup_stop(l);
+
+ t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key);
+ AVAHI_LLIST_REMOVE(AvahiMulticastLookup, by_key, t, l);
+ if (t)
+ avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t);
+ else
+ avahi_hashmap_remove(l->engine->lookups_by_key, l->key);
+
+ AVAHI_LLIST_REMOVE(AvahiMulticastLookup, lookups, l->engine->lookups, l);
+
+ if (l->key)
+ avahi_key_unref(l->key);
+
+ if (l->cname_key)
+ avahi_key_unref(l->cname_key);
+
+ avahi_free(l);
+}
+
+void avahi_multicast_lookup_free(AvahiMulticastLookup *l) {
+ assert(l);
+
+ if (l->dead)
+ return;
+
+ l->dead = 1;
+ l->engine->cleanup_dead = 1;
+ lookup_stop(l);
+}
+
+void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e) {
+ AvahiMulticastLookup *l, *n;
+ assert(e);
+
+ while (e->cleanup_dead) {
+ e->cleanup_dead = 0;
+
+ for (l = e->lookups; l; l = n) {
+ n = l->lookups_next;
+
+ if (l->dead)
+ lookup_destroy(l);
+ }
+ }
+}
+
+struct cbdata {
+ AvahiMulticastLookupEngine *engine;
+ AvahiMulticastLookupCallback callback;
+ void *userdata;
+ AvahiKey *key, *cname_key;
+ AvahiInterface *interface;
+ unsigned n_found;
+};
+
+static void* scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
+ struct cbdata *cbdata = userdata;
+
+ assert(c);
+ assert(pattern);
+ assert(e);
+ assert(cbdata);
+
+ cbdata->callback(
+ cbdata->engine,
+ cbdata->interface->hardware->index,
+ cbdata->interface->protocol,
+ AVAHI_BROWSER_NEW,
+ AVAHI_LOOKUP_CALLBACK_CACHED|AVAHI_LOOKUP_CALLBACK_MULTICAST,
+ e->record,
+ cbdata->userdata);
+
+ cbdata->n_found ++;
+
+ return NULL;
+}
+
+static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
+ struct cbdata *cbdata = userdata;
+
+ assert(m);
+ assert(i);
+ assert(cbdata);
+
+ cbdata->interface = i;
+
+ avahi_cache_walk(i->cache, cbdata->key, scan_cache_callback, cbdata);
+
+ if (cbdata->cname_key)
+ avahi_cache_walk(i->cache, cbdata->cname_key, scan_cache_callback, cbdata);
+
+ cbdata->interface = NULL;
+}
+
+unsigned avahi_multicast_lookup_engine_scan_cache(
+ AvahiMulticastLookupEngine *e,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiKey *key,
+ AvahiMulticastLookupCallback callback,
+ void *userdata) {
+
+ struct cbdata cbdata;
+
+ assert(e);
+ assert(key);
+ assert(callback);
+
+ assert(AVAHI_IF_VALID(interface));
+ assert(AVAHI_PROTO_VALID(protocol));
+
+ cbdata.engine = e;
+ cbdata.key = key;
+ cbdata.cname_key = avahi_key_new_cname(key);
+ cbdata.callback = callback;
+ cbdata.userdata = userdata;
+ cbdata.interface = NULL;
+ cbdata.n_found = 0;
+
+ avahi_interface_monitor_walk(e->server->monitor, interface, protocol, scan_interface_callback, &cbdata);
+
+ if (cbdata.cname_key)
+ avahi_key_unref(cbdata.cname_key);
+
+ return cbdata.n_found;
+}
+
+void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i) {
+ AvahiMulticastLookup *l;
+
+ assert(e);
+ assert(i);
+
+ for (l = e->lookups; l; l = l->lookups_next) {
+
+ if (l->dead || !l->callback)
+ continue;
+
+ if (l->queriers_added && avahi_interface_match(i, l->interface, l->protocol))
+ avahi_querier_add(i, l->key, NULL);
+ }
+}
+
+void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event) {
+ AvahiMulticastLookup *l;
+
+ assert(e);
+ assert(record);
+ assert(i);
+
+ for (l = avahi_hashmap_lookup(e->lookups_by_key, record->key); l; l = l->by_key_next) {
+ if (l->dead || !l->callback)
+ continue;
+
+ if (avahi_interface_match(i, l->interface, l->protocol))
+ l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_CALLBACK_MULTICAST, record, l->userdata);
+ }
+
+
+ if (record->key->clazz == AVAHI_DNS_CLASS_IN && record->key->type == AVAHI_DNS_TYPE_CNAME) {
+ /* It's a CNAME record, so we have to scan the all lookups to see if one matches */
+
+ for (l = e->lookups; l; l = l->lookups_next) {
+ AvahiKey *key;
+
+ if (l->dead || !l->callback)
+ continue;
+
+ if ((key = avahi_key_new_cname(l->key))) {
+ if (avahi_key_equal(record->key, key))
+ l->callback(e, i->hardware->index, i->protocol, event, AVAHI_LOOKUP_CALLBACK_MULTICAST, record, l->userdata);
+
+ avahi_key_unref(key);
+ }
+ }
+ }
+}
+
+AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s) {
+ AvahiMulticastLookupEngine *e;
+
+ assert(s);
+
+ e = avahi_new(AvahiMulticastLookupEngine, 1);
+ e->server = s;
+ e->cleanup_dead = 0;
+
+ /* Initialize lookup list */
+ e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL);
+ AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups);
+
+ return e;
+}
+
+void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e) {
+ assert(e);
+
+ while (e->lookups)
+ lookup_destroy(e->lookups);
+
+ avahi_hashmap_free(e->lookups_by_key);
+ avahi_free(e);
+}
+
diff --git a/avahi-core/multicast-lookup.h b/avahi-core/multicast-lookup.h
new file mode 100644
index 0000000..43e240d
--- /dev/null
+++ b/avahi-core/multicast-lookup.h
@@ -0,0 +1,53 @@
+#ifndef foomulticastlookuphfoo
+#define foomulticastlookuphfoo
+
+/* $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
+ USA.
+***/
+
+#include "lookup.h"
+#include "browse.h"
+
+typedef struct AvahiMulticastLookupEngine AvahiMulticastLookupEngine;
+typedef struct AvahiMulticastLookup AvahiMulticastLookup;
+
+typedef void (*AvahiMulticastLookupCallback)(
+ AvahiMulticastLookupEngine *e,
+ AvahiIfIndex idx,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiLookupResultFlags flags,
+ AvahiRecord *r,
+ void *userdata);
+
+AvahiMulticastLookupEngine *avahi_multicast_lookup_engine_new(AvahiServer *s);
+void avahi_multicast_lookup_engine_free(AvahiMulticastLookupEngine *e);
+
+unsigned avahi_multicast_lookup_engine_scan_cache(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata);
+void avahi_multicast_lookup_engine_new_interface(AvahiMulticastLookupEngine *e, AvahiInterface *i);
+void avahi_multicast_lookup_engine_cleanup(AvahiMulticastLookupEngine *e);
+void avahi_multicast_lookup_engine_notify(AvahiMulticastLookupEngine *e, AvahiInterface *i, AvahiRecord *record, AvahiBrowserEvent event);
+
+AvahiMulticastLookup *avahi_multicast_lookup_new(AvahiMulticastLookupEngine *e, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, AvahiMulticastLookupCallback callback, void *userdata);
+void avahi_multicast_lookup_free(AvahiMulticastLookup *q);
+
+
+#endif
+
diff --git a/avahi-core/publish.h b/avahi-core/publish.h
new file mode 100644
index 0000000..4e632f8
--- /dev/null
+++ b/avahi-core/publish.h
@@ -0,0 +1,244 @@
+#ifndef foopublishhfoo
+#define foopublishhfoo
+
+/* $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
+ USA.
+***/
+
+/** \file publish.h Functions for publising local services and RRs */
+
+/** \example core-publish-service.c Example how to register a DNS-SD
+ * service using an embedded mDNS stack. It behaves like a network
+ * printer registering both an IPP and a BSD LPR service. */
+
+
+#include <avahi-common/cdecl.h>
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+AVAHI_C_DECL_BEGIN
+#endif
+
+/** A group of locally registered DNS RRs */
+typedef struct AvahiSEntryGroup AvahiSEntryGroup;
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+AVAHI_C_DECL_END
+#endif
+
+#include "core.h"
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+AVAHI_C_DECL_BEGIN
+#endif
+
+/** Prototype for callback functions which are called whenever the state of an AvahiSEntryGroup object changes */
+typedef void (*AvahiSEntryGroupCallback) (AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata);
+
+/** Iterate through all local entries of the server. (when g is NULL)
+ * or of a specified entry group. At the first call state should point
+ * to a NULL initialized void pointer, That pointer is used to track
+ * the current iteration. It is not safe to call any other
+ * avahi_server_xxx() function during the iteration. If the last entry
+ * has been read, NULL is returned. */
+const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state);
+
+/** Create a new entry group. The specified callback function is
+ * called whenever the state of the group changes. Use entry group
+ * objects to keep track of you RRs. Add new RRs to a group using
+ * avahi_server_add_xxx(). Make sure to call avahi_s_entry_group_commit()
+ * to start the registration process for your RRs */
+AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata);
+
+/** Free an entry group. All RRs assigned to the group are removed from the server */
+void avahi_s_entry_group_free(AvahiSEntryGroup *g);
+
+/** Commit an entry group. This starts the probing and registration process for all RRs in the group */
+int avahi_s_entry_group_commit(AvahiSEntryGroup *g);
+
+/** Remove all entries from the entry group and reset the state to AVAHI_ENTRY_GROUP_UNCOMMITED. */
+void avahi_s_entry_group_reset(AvahiSEntryGroup *g);
+
+/** Return 1 if the entry group is empty, i.e. has no records attached. */
+int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g);
+
+/** Return the current state of the specified entry group */
+AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g);
+
+/** Change the opaque user data pointer attached to an entry group object */
+void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata);
+
+/** Return the opaque user data pointer currently set for the entry group object */
+void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g);
+
+/** Add a new resource record to the server. Returns 0 on success, negative otherwise. */
+int avahi_server_add(
+ AvahiServer *s, /**< The server object to add this record to */
+ AvahiSEntryGroup *g, /**< An entry group object if this new record shall be attached to one, or NULL. If you plan to remove the record sometime later you a required to pass an entry group object here. */
+ AvahiIfIndex interface, /**< A numeric index of a network interface to attach this record to, or AVAHI_IF_UNSPEC to attach this record to all interfaces */
+ AvahiProtocol protocol, /**< A protocol family to attach this record to. One of the AVAHI_PROTO_xxx constants. Use AVAHI_PROTO_UNSPEC to make this record available on all protocols (wich means on both IPv4 and IPv6). */
+ AvahiEntryFlags flags, /**< Special flags for this record */
+ AvahiRecord *r /**< The record to add. This function increases the reference counter of this object. */ );
+
+/** Add a PTR RR to the server. See avahi_server_add() for more information. */
+int avahi_server_add_ptr(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiEntryFlags flags,
+ uint32_t ttl, /**< DNS TTL for this record */
+ const char *name, /**< PTR record name */
+ const char *dest /**< pointer destination */ );
+
+/** Add a PTR RR to the server. See avahi_server_add() for more information. */
+int avahi_server_add_txt(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiEntryFlags flags,
+ uint32_t ttl, /**< DNS TTL for this record */
+ const char *name, /**< TXT record name */
+ ... /**< Text record data, terminated by NULL */) AVAHI_GCC_SENTINEL;
+
+/** Add a PTR RR to the server. Mostly identical to
+ * avahi_server_add_text but takes a va_list instead of a variable
+ * number of arguments */
+int avahi_server_add_txt_va(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiEntryFlags flags,
+ uint32_t ttl,
+ const char *name,
+ va_list va);
+
+/** Add a PTR RR to the server. Mostly identical to
+ * avahi_server_add_text but takes an AvahiStringList record instead of a variable
+ * number of arguments. */
+int avahi_server_add_txt_strlst(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiEntryFlags flags,
+ uint32_t ttl,
+ const char *name,
+ AvahiStringList *strlst /**< TXT decord data as a AvahiString. This routine makes a deep copy of this object. */ );
+
+/** Add an IP address mapping to the server. This will add both the
+ * host-name-to-address and the reverse mapping to the server. See
+ * avahi_server_add() for more information. If adding one of the RRs
+ * fails, the function returns with an error, but it is not defined if
+ * the other RR is deleted from the server or not. Therefore, you have
+ * to free the AvahiSEntryGroup and create a new one before
+ * proceeding. */
+int avahi_server_add_address(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiEntryFlags flags,
+ const char *name,
+ AvahiAddress *a);
+
+/** Add an DNS-SD service to the Server. This will add all required
+ * RRs to the server. See avahi_server_add() for more information. If
+ * adding one of the RRs fails, the function returns with an error,
+ * but it is not defined if the other RR is deleted from the server or
+ * not. Therefore, you have to free the AvahiSEntryGroup and create a
+ * new one before proceeding. */
+int avahi_server_add_service(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *name, /**< Service name, e.g. "Lennart's Files" */
+ const char *type, /**< DNS-SD type, e.g. "_http._tcp" */
+ const char *domain,
+ const char *host, /**< Host name where this servcie resides, or NULL if on the local host */
+ uint16_t port, /**< Port number of the service */
+ ... /**< Text records, terminated by NULL */) AVAHI_GCC_SENTINEL;
+
+/** Mostly identical to avahi_server_add_service(), but takes an va_list for the TXT records. */
+int avahi_server_add_service_va(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ va_list va);
+
+/** Mostly identical to avahi_server_add_service(), but takes an AvahiStringList object for the TXT records. The AvahiStringList object is copied. */
+int avahi_server_add_service_strlst(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ AvahiStringList *strlst);
+
+/** The type of DNS server */
+typedef enum {
+ AVAHI_DNS_SERVER_RESOLVE, /**< Unicast DNS servers for normal resolves (_domain._udp)*/
+ AVAHI_DNS_SERVER_UPDATE /**< Unicast DNS servers for updates (_dns-update._udp)*/
+} AvahiDNSServerType;
+
+/** Publish the specified unicast DNS server address via mDNS. You may
+ * browse for records create this way wit
+ * avahi_s_dns_server_browser_new(). */
+int avahi_server_add_dns_server_address(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *domain,
+ AvahiDNSServerType type,
+ const AvahiAddress *address,
+ uint16_t port /** should be 53 */);
+
+/** Similar to avahi_server_add_dns_server_address(), but specify a
+host name instead of an address. The specified host name should be
+resolvable via mDNS */
+int avahi_server_add_dns_server_name(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *domain,
+ AvahiDNSServerType type,
+ const char *name,
+ uint16_t port /** should be 53 */);
+
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+AVAHI_C_DECL_END
+#endif
+
+#endif
diff --git a/avahi-core/querier-test.c b/avahi-core/querier-test.c
new file mode 100644
index 0000000..f410e66
--- /dev/null
+++ b/avahi-core/querier-test.c
@@ -0,0 +1,123 @@
+/* $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
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include <avahi-common/malloc.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/alternative.h>
+#include <avahi-core/core.h>
+#include <avahi-core/log.h>
+#include <avahi-core/publish.h>
+#include <avahi-core/lookup.h>
+
+#define DOMAIN NULL
+#define SERVICE_TYPE "_http._tcp"
+
+static AvahiSServiceBrowser *service_browser1 = NULL, *service_browser2 = NULL;
+static const AvahiPoll * poll_api = NULL;
+static AvahiServer *server = NULL;
+static AvahiSimplePoll *simple_poll;
+
+static const char *browser_event_to_string(AvahiBrowserEvent event) {
+ switch (event) {
+ case AVAHI_BROWSER_NEW : return "NEW";
+ case AVAHI_BROWSER_REMOVE : return "REMOVE";
+ case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CACHE_EXHAUSTED";
+ case AVAHI_BROWSER_ALL_FOR_NOW : return "ALL_FOR_NOW";
+ case AVAHI_BROWSER_FAILURE : return "FAILURE";
+ case AVAHI_BROWSER_NOT_FOUND : return "NOT_FOUND";
+ }
+
+ abort();
+}
+
+static void sb_callback(
+ AvahiSServiceBrowser *b,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *service_type,
+ const char *domain,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+ avahi_log_debug("SB%i: (%i.%s) <%s> as <%s> in <%s> [%s] cached=%i", b == service_browser1 ? 1 : 2, iface, avahi_proto_to_string(protocol), name, service_type, domain, browser_event_to_string(event), !!(flags & AVAHI_LOOKUP_CALLBACK_CACHED));
+}
+
+static void create_second_service_browser(AvahiTimeout *timeout, void* userdata) {
+
+ service_browser2 = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, DOMAIN, 0, sb_callback, NULL);
+ assert(service_browser2);
+
+ poll_api->timeout_free(timeout);
+}
+
+static void quit(AvahiTimeout *timeout, void *userdata) {
+ avahi_simple_poll_quit(simple_poll);
+}
+
+int main(int argc, char *argv[]) {
+ struct timeval tv;
+ AvahiServerConfig config;
+
+ simple_poll = avahi_simple_poll_new();
+ assert(simple_poll);
+
+ poll_api = avahi_simple_poll_get(simple_poll);
+ assert(poll_api);
+
+ avahi_server_config_init(&config);
+ config.publish_hinfo = 0;
+ config.publish_addresses = 0;
+ config.publish_workstation = 0;
+ config.publish_domain = 0;
+
+ avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &config.wide_area_servers[0]);
+ config.n_wide_area_servers = 1;
+ config.enable_wide_area = 1;
+
+ server = avahi_server_new(poll_api, &config, NULL, NULL, NULL);
+ assert(server);
+ avahi_server_config_free(&config);
+
+ service_browser1 = avahi_s_service_browser_new(server, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, SERVICE_TYPE, DOMAIN, 0, sb_callback, NULL);
+ assert(service_browser1);
+
+ poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 10000, 0), create_second_service_browser, NULL);
+
+ poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 60000, 0), quit, NULL);
+
+
+ for (;;)
+ if (avahi_simple_poll_iterate(simple_poll, -1) != 0)
+ break;
+
+ avahi_server_free(server);
+ avahi_simple_poll_free(simple_poll);
+
+ return 0;
+}
diff --git a/avahi-core/querier.c b/avahi-core/querier.c
new file mode 100644
index 0000000..51b2ce6
--- /dev/null
+++ b/avahi-core/querier.c
@@ -0,0 +1,200 @@
+/* $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
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+
+#include "querier.h"
+#include "log.h"
+
+struct AvahiQuerier {
+ AvahiInterface *interface;
+
+ AvahiKey *key;
+ int n_used;
+
+ unsigned sec_delay;
+
+ AvahiTimeEvent *time_event;
+
+ struct timeval creation_time;
+
+ AVAHI_LLIST_FIELDS(AvahiQuerier, queriers);
+};
+
+void avahi_querier_free(AvahiQuerier *q) {
+ assert(q);
+
+ AVAHI_LLIST_REMOVE(AvahiQuerier, queriers, q->interface->queriers, q);
+ avahi_hashmap_remove(q->interface->queriers_by_key, q->key);
+
+ avahi_key_unref(q->key);
+ avahi_time_event_free(q->time_event);
+
+ avahi_free(q);
+}
+
+static void querier_elapse_callback(AvahiTimeEvent *e, void *userdata) {
+ AvahiQuerier *q = userdata;
+ struct timeval tv;
+
+ assert(q);
+
+ avahi_interface_post_query(q->interface, q->key, 0);
+
+ q->sec_delay *= 2;
+
+ if (q->sec_delay >= 60*60) /* 1h */
+ q->sec_delay = 60*60;
+
+ avahi_elapse_time(&tv, q->sec_delay*1000, 0);
+ avahi_time_event_update(q->time_event, &tv);
+}
+
+void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime) {
+ AvahiQuerier *q;
+ struct timeval tv;
+
+ assert(i);
+ assert(key);
+
+ if ((q = avahi_hashmap_lookup(i->queriers_by_key, key))) {
+ /* Someone is already browsing for records of this RR key */
+ q->n_used++;
+
+ /* Return the creation time */
+ if (ret_ctime)
+ *ret_ctime = q->creation_time;
+ return;
+ }
+
+ /* No one is browsing for this RR key, so we add a new querier */
+ if (!(q = avahi_new(AvahiQuerier, 1)))
+ return; /* OOM */
+
+ q->key = avahi_key_ref(key);
+ q->interface = i;
+ q->n_used = 1;
+ q->sec_delay = 1;
+ gettimeofday(&q->creation_time, NULL);
+
+ /* Do the initial query */
+ avahi_interface_post_query(i, key, 0);
+
+ /* Schedule next queries */
+ q->time_event = avahi_time_event_new(i->monitor->server->time_event_queue, avahi_elapse_time(&tv, q->sec_delay*1000, 0), querier_elapse_callback, q);
+
+ AVAHI_LLIST_PREPEND(AvahiQuerier, queriers, i->queriers, q);
+ avahi_hashmap_insert(i->queriers_by_key, q->key, q);
+
+ /* Return the creation time */
+ if (ret_ctime)
+ *ret_ctime = q->creation_time;
+}
+
+void avahi_querier_remove(AvahiInterface *i, AvahiKey *key) {
+ AvahiQuerier *q;
+
+ if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key))) {
+ /* The was no querier for this RR key */
+ avahi_log_warn(__FILE__": querier_remove() called but no querier to remove");
+ return;
+ }
+
+ assert(q->n_used >= 1);
+ if ((--q->n_used) <= 0)
+ avahi_querier_free(q);
+}
+
+static void remove_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
+ assert(m);
+ assert(i);
+ assert(userdata);
+
+ if (i->announcing)
+ avahi_querier_remove(i, (AvahiKey*) userdata);
+}
+
+void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key) {
+ assert(s);
+ assert(key);
+
+ avahi_interface_monitor_walk(s->monitor, idx, protocol, remove_querier_callback, key);
+}
+
+struct cbdata {
+ AvahiKey *key;
+ struct timeval *ret_ctime;
+};
+
+static void add_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
+ struct cbdata *cbdata = userdata;
+
+ assert(m);
+ assert(i);
+ assert(cbdata);
+
+ if (i->announcing) {
+ struct timeval tv;
+ avahi_querier_add(i, cbdata->key, &tv);
+
+ if (cbdata->ret_ctime && avahi_timeval_compare(&tv, cbdata->ret_ctime) > 0)
+ *cbdata->ret_ctime = tv;
+ }
+}
+
+void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime) {
+ struct cbdata cbdata;
+
+ assert(s);
+ assert(key);
+
+ cbdata.key = key;
+ cbdata.ret_ctime = ret_ctime;
+
+ if (ret_ctime)
+ ret_ctime->tv_sec = ret_ctime->tv_usec = 0;
+
+ avahi_interface_monitor_walk(s->monitor, idx, protocol, add_querier_callback, &cbdata);
+}
+
+int avahi_querier_exists(AvahiInterface *i, AvahiKey *key) {
+ assert(i);
+ assert(key);
+
+ if (avahi_hashmap_lookup(i->queriers_by_key, key))
+ return 1;
+
+ return 0;
+}
+
+void avahi_querier_free_all(AvahiInterface *i) {
+ assert(i);
+
+ while (i->queriers)
+ avahi_querier_free(i->queriers);
+}
diff --git a/avahi-core/querier.h b/avahi-core/querier.h
new file mode 100644
index 0000000..3ec4564
--- /dev/null
+++ b/avahi-core/querier.h
@@ -0,0 +1,50 @@
+#ifndef fooquerierhfoo
+#define fooquerierhfoo
+
+/* $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
+ USA.
+***/
+
+typedef struct AvahiQuerier AvahiQuerier;
+
+#include "iface.h"
+
+/** Add querier for the specified key to the specified interface */
+void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_ctime);
+
+/** Remove a querier for the specified key from the specified interface */
+void avahi_querier_remove(AvahiInterface *i, AvahiKey *key);
+
+/** Add a querier for the specified key on all interfaces that mach */
+void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key, struct timeval *ret_ctime);
+
+/** Remove a querier for the specified key on all interfaces that mach */
+void avahi_querier_remove_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol protocol, AvahiKey *key);
+
+/** Free all queriers */
+void avahi_querier_free(AvahiQuerier *q);
+
+/** Free all queriers on the specified interface */
+void avahi_querier_free_all(AvahiInterface *i);
+
+/** Return 1 if there is a querier for the specified key on the specified interface */
+int avahi_querier_exists(AvahiInterface *i, AvahiKey *key);
+
+#endif
diff --git a/avahi-core/resolve-address.c b/avahi-core/resolve-address.c
index d5fe743..4fb588e 100644
--- a/avahi-core/resolve-address.c
+++ b/avahi-core/resolve-address.c
@@ -29,7 +29,7 @@
#include "browse.h"
-#define TIMEOUT_MSEC 1000
+#define TIMEOUT_MSEC 5000
struct AvahiSAddressResolver {
AvahiServer *server;
@@ -43,6 +43,10 @@ struct AvahiSAddressResolver {
AvahiRecord *ptr_record;
AvahiIfIndex interface;
AvahiProtocol protocol;
+ AvahiLookupResultFlags flags;
+
+ int retry_with_multicast;
+ AvahiKey *key;
AvahiTimeEvent *time_event;
@@ -57,14 +61,17 @@ static void finish(AvahiSAddressResolver *r, AvahiResolverEvent event) {
r->time_event = NULL;
}
- if (event == AVAHI_RESOLVER_TIMEOUT)
- r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_TIMEOUT, &r->address, NULL, r->userdata);
- else {
-
- assert(event == AVAHI_RESOLVER_FOUND);
- assert(r->ptr_record);
-
- r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, &r->address, r->ptr_record->data.ptr.name, r->userdata);
+ switch (event) {
+ case AVAHI_RESOLVER_NOT_FOUND:
+ case AVAHI_RESOLVER_TIMEOUT:
+ 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;
}
}
@@ -88,51 +95,98 @@ static void start_timeout(AvahiSAddressResolver *r) {
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, void* userdata) {
+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(record);
assert(r);
- assert(record->key->type == AVAHI_DNS_TYPE_PTR);
-
- if (event == AVAHI_BROWSER_NEW) {
-
- 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);
-
- finish(r, AVAHI_RESOLVER_FOUND);
- }
- } else {
-
- assert(event == AVAHI_BROWSER_REMOVE);
-
- if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) {
- avahi_record_unref(r->ptr_record);
- r->ptr_record = NULL;
-
- /** Look for a replacement */
- avahi_s_record_browser_restart(r->record_browser);
- start_timeout(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_NOT_FOUND:
+
+ 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_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_MULTICAST, record_browser_callback, r);
+
+ if (r->record_browser) {
+ start_timeout(r);
+ break;
+ }
+ }
+
+ /* Fallthrough */
+
+ case AVAHI_BROWSER_FAILURE:
+ r->flags = flags;
+ finish(r, event == AVAHI_BROWSER_NOT_FOUND ? AVAHI_RESOLVER_NOT_FOUND : AVAHI_RESOLVER_FAILURE);
+
}
}
-AvahiSAddressResolver *avahi_s_address_resolver_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const AvahiAddress *address, AvahiSAddressResolverCallback callback, void* userdata) {
+AvahiSAddressResolver *avahi_s_address_resolver_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const AvahiAddress *address,
+ AvahiLookupFlags flags,
+ AvahiSAddressResolverCallback callback,
+ void* userdata) {
+
AvahiSAddressResolver *r;
AvahiKey *k;
char *n;
@@ -143,6 +197,16 @@ AvahiSAddressResolver *avahi_s_address_resolver_new(AvahiServer *server, AvahiIf
assert(address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6);
+ if (!AVAHI_IF_VALID(interface)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_INTERFACE);
+ return NULL;
+ }
+
+ if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS);
+ return NULL;
+ }
+
if (address->proto == AVAHI_PROTO_INET)
n = avahi_reverse_lookup_name_ipv4(&address->data.ipv4);
else
@@ -174,20 +238,33 @@ AvahiSAddressResolver *avahi_s_address_resolver_new(AvahiServer *server, AvahiIf
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;
- start_timeout(r);
+
+ 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_new(server, interface, protocol, k, record_browser_callback, r);
- avahi_key_unref(k);
+ r->record_browser = avahi_s_record_browser_new(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;
}
@@ -205,6 +282,9 @@ void avahi_s_address_resolver_free(AvahiSAddressResolver *r) {
if (r->ptr_record)
avahi_record_unref(r->ptr_record);
+
+ if (r->key)
+ avahi_key_unref(r->key);
avahi_free(r);
}
diff --git a/avahi-core/resolve-host-name.c b/avahi-core/resolve-host-name.c
index 7bb5727..622ece1 100644
--- a/avahi-core/resolve-host-name.c
+++ b/avahi-core/resolve-host-name.c
@@ -33,7 +33,7 @@
#include "browse.h"
#include "log.h"
-#define TIMEOUT_MSEC 1000
+#define TIMEOUT_MSEC 5000
struct AvahiSHostNameResolver {
AvahiServer *server;
@@ -48,6 +48,7 @@ struct AvahiSHostNameResolver {
AvahiRecord *address_record;
AvahiIfIndex interface;
AvahiProtocol protocol;
+ AvahiLookupResultFlags flags;
AvahiTimeEvent *time_event;
@@ -62,30 +63,38 @@ static void finish(AvahiSHostNameResolver *r, AvahiResolverEvent event) {
r->time_event = NULL;
}
- if (event == AVAHI_RESOLVER_TIMEOUT)
- r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_TIMEOUT, r->host_name, NULL, r->userdata);
- else {
- AvahiAddress a;
-
- assert(event == AVAHI_RESOLVER_FOUND);
- 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();
- }
+ 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;
- r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, r->address_record->key->name, &a, r->userdata);
+ }
+
+ case AVAHI_RESOLVER_TIMEOUT:
+ case AVAHI_RESOLVER_NOT_FOUND:
+ case AVAHI_RESOLVER_FAILURE:
+
+ r->callback(r, r->interface, r->protocol, event, r->host_name, NULL, r->flags, r->userdata);
+ break;
}
}
@@ -106,71 +115,130 @@ static void start_timeout(AvahiSHostNameResolver *r) {
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, void* userdata) {
+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(record);
assert(r);
- assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA);
-
- if (event == AVAHI_BROWSER_NEW) {
-
- 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);
+
+ 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;
- finish(r, AVAHI_RESOLVER_FOUND);
- }
- } else {
+ 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);
+ }
- assert(event == AVAHI_BROWSER_REMOVE);
+ break;
- if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) {
- avahi_record_unref(r->address_record);
- r->address_record = NULL;
+ case AVAHI_BROWSER_REMOVE:
+ assert(record);
+ assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA);
- /** Look for a replacement */
+ 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:
+ case AVAHI_BROWSER_NOT_FOUND:
+
+ /* Stop browsers */
+
if (r->record_browser_aaaa)
- avahi_s_record_browser_restart(r->record_browser_aaaa);
+ avahi_s_record_browser_free(r->record_browser_aaaa);
if (r->record_browser_a)
- avahi_s_record_browser_restart(r->record_browser_a);
+ avahi_s_record_browser_free(r->record_browser_a);
- start_timeout(r);
- }
+ r->record_browser_a = r->record_browser_aaaa = NULL;
+ r->flags = flags;
+
+ finish(r, event == AVAHI_BROWSER_FAILURE ? AVAHI_RESOLVER_FAILURE : AVAHI_RESOLVER_NOT_FOUND);
+ break;
}
}
-AvahiSHostNameResolver *avahi_s_host_name_resolver_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *host_name, AvahiProtocol aprotocol, AvahiSHostNameResolverCallback callback, void* userdata) {
+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 *r;
AvahiKey *k;
-
+
assert(server);
assert(host_name);
assert(callback);
assert(aprotocol == AVAHI_PROTO_UNSPEC || aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_INET6);
+ if (!AVAHI_IF_VALID(interface)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_INTERFACE);
+ return NULL;
+ }
+
if (!avahi_is_valid_domain_name(host_name)) {
avahi_server_set_errno(server, AVAHI_ERR_INVALID_HOST_NAME);
return NULL;
}
-
+
+ if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS);
+ return NULL;
+ }
+
if (!(r = avahi_new(AvahiSHostNameResolver, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
return NULL;
@@ -183,19 +251,20 @@ AvahiSHostNameResolver *avahi_s_host_name_resolver_new(AvahiServer *server, Avah
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;
- start_timeout(r);
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_new(server, interface, protocol, k, record_browser_callback, r);
+ r->record_browser_a = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r);
avahi_key_unref(k);
if (!r->record_browser_a)
@@ -204,7 +273,7 @@ AvahiSHostNameResolver *avahi_s_host_name_resolver_new(AvahiServer *server, Avah
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_new(server, interface, protocol, k, record_browser_callback, r);
+ r->record_browser_aaaa = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r);
avahi_key_unref(k);
if (!r->record_browser_aaaa)
@@ -213,6 +282,8 @@ AvahiSHostNameResolver *avahi_s_host_name_resolver_new(AvahiServer *server, Avah
assert(r->record_browser_aaaa || r->record_browser_a);
+ start_timeout(r);
+
return r;
fail:
diff --git a/avahi-core/resolve-service.c b/avahi-core/resolve-service.c
index 0ecd388..31b0081 100644
--- a/avahi-core/resolve-service.c
+++ b/avahi-core/resolve-service.c
@@ -33,8 +33,9 @@
#include <avahi-common/error.h>
#include "browse.h"
+#include "log.h"
-#define TIMEOUT_MSEC 1000
+#define TIMEOUT_MSEC 5000
struct AvahiSServiceResolver {
AvahiServer *server;
@@ -52,9 +53,11 @@ struct AvahiSServiceResolver {
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;
@@ -62,6 +65,8 @@ struct AvahiSServiceResolver {
};
static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) {
+ AvahiLookupResultFlags flags;
+
assert(r);
if (r->time_event) {
@@ -69,44 +74,74 @@ static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) {
r->time_event = NULL;
}
- if (event == AVAHI_RESOLVER_TIMEOUT)
- r->callback(r, r->interface, r->protocol, event, r->service_name, r->service_type, r->domain_name, NULL, NULL, 0, NULL, r->userdata);
- else {
- AvahiAddress a;
- char sn[256], st[256];
- size_t i;
-
- assert(event == AVAHI_RESOLVER_FOUND);
-
- assert(r->srv_record);
- assert(r->txt_record);
- 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:
- assert(0);
+ flags =
+ r->txt_flags |
+ r->srv_flags |
+ r->address_flags;
+
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ case AVAHI_RESOLVER_NOT_FOUND:
+ case AVAHI_RESOLVER_TIMEOUT:
+
+ 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->address_record ? r->address_record->key->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;
}
-
- snprintf(sn, sizeof(sn), r->service_name);
- snprintf(st, sizeof(st), r->service_type);
-
- if ((i = strlen(sn)) > 0 && sn[i-1] == '.')
- sn[i-1] = 0;
-
- if ((i = strlen(st)) > 0 && st[i-1] == '.')
- st[i-1] = 0;
-
- r->callback(r, r->interface, r->protocol, event, sn, st, r->domain_name, r->srv_record->data.srv.name, &a, r->srv_record->data.srv.port, r->txt_record->data.txt.string_list, r->userdata);
}
}
@@ -116,6 +151,8 @@ static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
assert(e);
assert(r);
+ avahi_log_debug("timeout");
+
finish(r, AVAHI_RESOLVER_TIMEOUT);
}
@@ -125,152 +162,237 @@ static void start_timeout(AvahiSServiceResolver *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, void* userdata) {
+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(record);
assert(r);
- if (event == AVAHI_BROWSER_NEW) {
- int changed = 0;
-
- 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;
-
- 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 (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 != 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;
+
+ 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_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r);
+ 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_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r);
+ avahi_key_unref(k);
+ }
+ }
}
+ break;
+
+ case AVAHI_DNS_TYPE_TXT:
- if (r->record_browser_aaaa) {
- avahi_s_record_browser_free(r->record_browser_aaaa);
- r->record_browser_aaaa = NULL;
- }
-
- 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_new(r->server, r->interface, r->protocol, k, record_browser_callback, r);
- 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_new(r->server, r->interface, r->protocol, k, record_browser_callback, r);
- avahi_key_unref(k);
+ assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT));
+
+ if (!r->txt_record) {
+ r->txt_record = avahi_record_ref(record);
+ changed = 1;
}
- }
- break;
+ break;
- case AVAHI_DNS_TYPE_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:
- if (!r->address_record) {
- r->address_record = avahi_record_ref(record);
- changed = 1;
- }
- break;
-
- default:
- abort();
- }
+ case AVAHI_DNS_TYPE_A:
+ case AVAHI_DNS_TYPE_AAAA:
+ assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS));
- if (changed && r->txt_record && r->srv_record && r->address_record)
- finish(r, AVAHI_RESOLVER_FOUND);
+ if (!r->address_record) {
+ r->address_record = avahi_record_ref(record);
+ changed = 1;
+ }
+ break;
+
+ default:
+ abort();
+ }
- } else {
- assert(event == AVAHI_BROWSER_REMOVE);
-
- switch (record->key->type) {
- case AVAHI_DNS_TYPE_SRV:
+ 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);
- if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) {
- avahi_record_unref(r->srv_record);
- r->srv_record = NULL;
+ break;
- /** Look for a replacement */
- avahi_s_record_browser_restart(r->record_browser_srv);
- start_timeout(r);
- }
-
- break;
+ }
+
+ case AVAHI_BROWSER_REMOVE:
- case AVAHI_DNS_TYPE_TXT:
+ assert(record);
- if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) {
- avahi_record_unref(r->txt_record);
- r->txt_record = NULL;
+ 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();
+ }
- /** Look for a replacement */
- avahi_s_record_browser_restart(r->record_browser_txt);
- start_timeout(r);
- }
- break;
+ break;
- case AVAHI_DNS_TYPE_A:
- case AVAHI_DNS_TYPE_AAAA:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ break;
- if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) {
- avahi_record_unref(r->address_record);
- r->address_record = NULL;
+ case AVAHI_BROWSER_NOT_FOUND:
+ case AVAHI_BROWSER_FAILURE:
- /** 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;
+ 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);
- default:
- abort();
- }
+ r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL;
+
+ finish(r, event == AVAHI_BROWSER_FAILURE ? AVAHI_RESOLVER_FAILURE : AVAHI_RESOLVER_NOT_FOUND);
+ break;
}
}
-AvahiSServiceResolver *avahi_s_service_resolver_new(AvahiServer *server, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiProtocol aprotocol, AvahiSServiceResolverCallback callback, void* userdata) {
+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 *r;
AvahiKey *k;
- char t[256], *n;
- size_t l;
+ char t[256];
assert(server);
- assert(name);
assert(type);
assert(callback);
assert(aprotocol == AVAHI_PROTO_UNSPEC || aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_INET6);
- if (!avahi_is_valid_service_name(name)) {
+ if (!AVAHI_IF_VALID(interface)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_INTERFACE);
+ return NULL;
+ }
+
+ if (name && !avahi_is_valid_service_name(name)) {
avahi_server_set_errno(server, AVAHI_ERR_INVALID_SERVICE_NAME);
return NULL;
}
@@ -280,10 +402,18 @@ AvahiSServiceResolver *avahi_s_service_resolver_new(AvahiServer *server, AvahiIf
return NULL;
}
- if (!avahi_is_valid_domain_name(domain)) {
+ if (domain && !avahi_is_valid_domain_name(domain)) {
avahi_server_set_errno(server, AVAHI_ERR_INVALID_DOMAIN_NAME);
return NULL;
}
+
+ if (!domain)
+ domain = server->domain_name;
+
+ if (!AVAHI_VALID_FLAGS(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS)) {
+ avahi_server_set_errno(server, AVAHI_ERR_INVALID_FLAGS);
+ return NULL;
+ }
if (!(r = avahi_new(AvahiSServiceResolver, 1))) {
avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
@@ -298,39 +428,50 @@ AvahiSServiceResolver *avahi_s_service_resolver_new(AvahiServer *server, AvahiIf
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;
-
- n = t;
- l = sizeof(t);
- avahi_escape_label((const uint8_t*) name, strlen(name), &n, &l);
- snprintf(n, l, ".%s.%s", r->service_type, r->domain_name);
+ r->user_flags = flags;
+
+ if (name) {
+ char *n;
+ size_t l;
+
+ n = t;
+ l = sizeof(t);
+ avahi_escape_label((const uint8_t*) name, strlen(name), &n, &l);
+ snprintf(n, l, ".%s.%s", r->service_type, r->domain_name);
+ } else
+ snprintf(t, sizeof(t), "%s.%s", r->service_type, r->domain_name);
r->time_event = NULL;
- start_timeout(r);
AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r);
r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL;
k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
- r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r);
+ 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;
}
-
- k = avahi_key_new(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
- r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, record_browser_callback, r);
- avahi_key_unref(k);
- if (!r->record_browser_txt) {
- avahi_s_service_resolver_free(r);
- return NULL;
+ if (!(flags & AVAHI_LOOKUP_NO_TXT)) {
+ k = avahi_key_new(t, 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;
}
diff --git a/avahi-core/rr.c b/avahi-core/rr.c
index 704f5be..90c6236 100644
--- a/avahi-core/rr.c
+++ b/avahi-core/rr.c
@@ -60,6 +60,19 @@ AvahiKey *avahi_key_new(const char *name, uint16_t class, uint16_t type) {
return k;
}
+AvahiKey *avahi_key_new_cname(AvahiKey *key) {
+ assert(key);
+
+ if (key->clazz != AVAHI_DNS_CLASS_IN)
+ return NULL;
+
+ if (key->type == AVAHI_DNS_TYPE_CNAME)
+ return NULL;
+
+ return avahi_key_new(key->name, key->clazz, AVAHI_DNS_TYPE_CNAME);
+}
+
+
AvahiKey *avahi_key_ref(AvahiKey *k) {
assert(k);
assert(k->ref >= 1);
diff --git a/avahi-core/rr.h b/avahi-core/rr.h
index 40134bf..6824d2a 100644
--- a/avahi-core/rr.h
+++ b/avahi-core/rr.h
@@ -124,6 +124,9 @@ typedef struct {
/** Create a new AvahiKey object. The reference counter will be set to 1. */
AvahiKey *avahi_key_new(const char *name, uint16_t clazz, uint16_t type);
+/** Creaze new AvahiKey object based on an existing key but replaceing the type by CNAME */
+AvahiKey *avahi_key_new_cname(AvahiKey *key);
+
/** Increase the reference counter of an AvahiKey object by one */
AvahiKey *avahi_key_ref(AvahiKey *k);
diff --git a/avahi-core/server.c b/avahi-core/server.c
index c746287..2714298 100644
--- a/avahi-core/server.c
+++ b/avahi-core/server.c
@@ -953,7 +953,7 @@ static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sock
if (s->config.enable_reflector)
from_local_iface = originates_from_local_iface(s, iface, &a, port);
- if (avahi_dns_packet_is_valid(p) < 0) {
+ if (avahi_dns_packet_check_valid_multicast(p) < 0) {
avahi_log_warn("Recieved invalid packet.");
return;
}
@@ -1013,10 +1013,9 @@ static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sock
}
}
-static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface, int ttl) {
+static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, const struct sockaddr *sa, AvahiIfIndex iface) {
AvahiInterface *i, *j;
AvahiAddress a;
- uint16_t port;
AvahiLegacyUnicastReflectSlot *slot;
assert(s);
@@ -1032,14 +1031,13 @@ static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p, co
/* avahi_log_debug("new legacy unicast packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol); */
- port = avahi_port_from_sockaddr(sa);
avahi_address_from_sockaddr(sa, &a);
if (avahi_address_is_ipv4_in_ipv6(&a))
/* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
return;
- if (avahi_dns_packet_is_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
+ if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
avahi_log_warn("Recieved invalid packet.");
return;
}
@@ -1094,22 +1092,21 @@ static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *us
dest.proto = AVAHI_PROTO_INET;
if ((p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, &sa, &dest.data.ipv4, &iface, &ttl))) {
- dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
+ dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa, iface);
avahi_dns_packet_free(p);
}
} else if (fd == s->fd_legacy_unicast_ipv6) {
dest.proto = AVAHI_PROTO_INET6;
if ((p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, &sa6, &dest.data.ipv6, &iface, &ttl))) {
- dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
+ dispatch_legacy_unicast_packet(s, p, (struct sockaddr*) &sa6, iface);
avahi_dns_packet_free(p);
}
}
cleanup_dead(s);
- } else {
- assert(0);
- }
+ } else
+ abort();
}
static void server_set_state(AvahiServer *s, AvahiServerState state) {
@@ -1335,20 +1332,24 @@ static int setup_sockets(AvahiServer *s) {
else if (s->fd_ipv6 < 0 && s->config.use_ipv6)
avahi_log_notice("Failed to create IPv6 socket, proceeding in IPv4 only mode");
- s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv4() : -1;
- s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_legacy_unicast_socket_ipv6() : -1;
-
- s->watch_ipv4 = s->watch_ipv6 = s->watch_legacy_unicast_ipv4 = s->watch_legacy_unicast_ipv6 = NULL;
+ s->fd_legacy_unicast_ipv4 = s->fd_ipv4 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv4() : -1;
+ s->fd_legacy_unicast_ipv6 = s->fd_ipv6 >= 0 && s->config.enable_reflector ? avahi_open_unicast_socket_ipv6() : -1;
+
+ s->watch_ipv4 =
+ s->watch_ipv6 =
+ s->watch_legacy_unicast_ipv4 =
+ s->watch_legacy_unicast_ipv6 = NULL;
if (s->fd_ipv4 >= 0)
s->watch_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_ipv4, AVAHI_WATCH_IN, socket_event, s);
if (s->fd_ipv6 >= 0)
s->watch_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_ipv6, AVAHI_WATCH_IN, socket_event, s);
+
if (s->fd_legacy_unicast_ipv4 >= 0)
s->watch_legacy_unicast_ipv4 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv4, AVAHI_WATCH_IN, socket_event, s);
if (s->fd_legacy_unicast_ipv6 >= 0)
s->watch_legacy_unicast_ipv6 = s->poll_api->watch_new(s->poll_api, s->fd_legacy_unicast_ipv6, AVAHI_WATCH_IN, socket_event, s);
-
+
return 0;
}
@@ -1386,6 +1387,7 @@ AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig
return NULL;
}
+
s->n_host_rr_pending = 0;
s->need_entry_cleanup = 0;
s->need_group_cleanup = 0;
@@ -1413,6 +1415,15 @@ AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig
s->legacy_unicast_reflect_slots = NULL;
s->legacy_unicast_reflect_id = 0;
+
+ if (s->config.enable_wide_area) {
+ s->wide_area_lookup_engine = avahi_wide_area_engine_new(s);
+ avahi_wide_area_set_servers(s->wide_area_lookup_engine, s->config.wide_area_servers, s->config.n_wide_area_servers);
+ } else
+ s->wide_area_lookup_engine = NULL;
+
+ s->multicast_lookup_engine = avahi_multicast_lookup_engine_new(s);
+
do {
s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
} while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
@@ -1445,15 +1456,7 @@ AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig
void avahi_server_free(AvahiServer* s) {
assert(s);
- while(s->entries)
- free_entry(s, s->entries);
-
- avahi_interface_monitor_free(s->monitor);
-
- while (s->groups)
- free_group(s, s->groups);
-
- free_slots(s);
+ /* Remove all browsers */
while (s->dns_server_browsers)
avahi_s_dns_server_browser_free(s->dns_server_browsers);
@@ -1472,31 +1475,54 @@ void avahi_server_free(AvahiServer* s) {
while (s->record_browsers)
avahi_s_record_browser_destroy(s->record_browsers);
- avahi_hashmap_free(s->record_browser_hashmap);
- avahi_hashmap_free(s->entries_by_key);
+ /* Remove all locally rgeistered stuff */
- avahi_time_event_queue_free(s->time_event_queue);
+ while(s->entries)
+ free_entry(s, s->entries);
+
+ avahi_interface_monitor_free(s->monitor);
+
+ while (s->groups)
+ free_group(s, s->groups);
+
+ free_slots(s);
+ avahi_hashmap_free(s->entries_by_key);
avahi_record_list_free(s->record_list);
+ avahi_hashmap_free(s->record_browser_hashmap);
+
+ if (s->wide_area_lookup_engine)
+ avahi_wide_area_engine_free(s->wide_area_lookup_engine);
+ avahi_multicast_lookup_engine_free(s->multicast_lookup_engine);
+ avahi_time_event_queue_free(s->time_event_queue);
+
+ /* Free watches */
+
if (s->watch_ipv4)
s->poll_api->watch_free(s->watch_ipv4);
if (s->watch_ipv6)
s->poll_api->watch_free(s->watch_ipv6);
+
if (s->watch_legacy_unicast_ipv4)
s->poll_api->watch_free(s->watch_legacy_unicast_ipv4);
if (s->watch_legacy_unicast_ipv6)
s->poll_api->watch_free(s->watch_legacy_unicast_ipv6);
+
+ /* Free sockets */
if (s->fd_ipv4 >= 0)
close(s->fd_ipv4);
if (s->fd_ipv6 >= 0)
close(s->fd_ipv6);
+
if (s->fd_legacy_unicast_ipv4 >= 0)
close(s->fd_legacy_unicast_ipv4);
if (s->fd_legacy_unicast_ipv6 >= 0)
close(s->fd_legacy_unicast_ipv6);
-
+
+ /* Free other stuff */
+
avahi_free(s->host_name);
avahi_free(s->domain_name);
avahi_free(s->host_name_fqdn);
@@ -1631,6 +1657,9 @@ int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata
}
avahi_dump_caches(s->monitor, callback, userdata);
+
+ if (s->wide_area_lookup_engine)
+ avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata);
return AVAHI_OK;
}
@@ -2059,7 +2088,7 @@ int avahi_server_add_dns_server_address(
AvahiRecord *r;
int ret;
- char n[64] = "ip-";
+ char n[64], h[64];
assert(s);
assert(address);
@@ -2073,11 +2102,13 @@ int avahi_server_add_dns_server_address(
return avahi_server_set_errno(s, AVAHI_ERR_INVALID_DOMAIN_NAME);
if (address->proto == AVAHI_PROTO_INET) {
- hexstring(n+3, sizeof(n)-3, &address->data, 4);
+ hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
+ snprintf(n, sizeof(n), "ip-%s.%s", h, s->domain_name);
r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
r->data.a.address = address->data.ipv4;
} else {
- hexstring(n+3, sizeof(n)-3, &address->data, 6);
+ hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address));
+ snprintf(n, sizeof(n), "ip6-%s.%s", h, s->domain_name);
r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
r->data.aaaa.address = address->data.ipv6;
}
@@ -2413,6 +2444,8 @@ AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
c->enable_reflector = 0;
c->reflect_ipv = 0;
c->add_service_cookie = 1;
+ c->enable_wide_area = 0;
+ c->n_wide_area_servers = 0;
return c;
}
@@ -2530,3 +2563,14 @@ fail:
return ret;
}
+
+/** Set the wide area DNS servers */
+int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n) {
+ assert(s);
+
+ if (!s->wide_area_lookup_engine)
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_CONFIG);
+
+ avahi_wide_area_set_servers(s->wide_area_lookup_engine, a, n);
+ return AVAHI_OK;
+}
diff --git a/avahi-core/server.h b/avahi-core/server.h
index 7384e98..600e442 100644
--- a/avahi-core/server.h
+++ b/avahi-core/server.h
@@ -37,6 +37,8 @@ typedef struct AvahiEntry AvahiEntry;
#include "dns.h"
#include "rrlist.h"
#include "hashmap.h"
+#include "wide-area.h"
+#include "multicast-lookup.h"
#define AVAHI_MAX_LEGACY_UNICAST_REFLECT_SLOTS 100
@@ -138,9 +140,14 @@ struct AvahiServer {
AvahiLegacyUnicastReflectSlot **legacy_unicast_reflect_slots;
uint16_t legacy_unicast_reflect_id;
+ /* The last error code */
int error;
+ /* The local service cookie */
uint32_t local_service_cookie;
+
+ AvahiMulticastLookupEngine *multicast_lookup_engine;
+ AvahiWideAreaLookupEngine *wide_area_lookup_engine;
};
int avahi_server_entry_match_interface(AvahiEntry *e, AvahiInterface *i);
diff --git a/avahi-core/socket.c b/avahi-core/socket.c
index 91cbf40..ecc137b 100644
--- a/avahi-core/socket.c
+++ b/avahi-core/socket.c
@@ -362,12 +362,11 @@ int avahi_send_dns_packet_ipv4(int fd, int interface, AvahiDnsPacket *p, const A
struct msghdr msg;
struct iovec io;
struct cmsghdr *cmsg;
- struct in_pktinfo *pkti;
uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
assert(fd >= 0);
assert(p);
- assert(avahi_dns_packet_is_valid(p) >= 0);
+ assert(avahi_dns_packet_check_valid(p) >= 0);
assert(!a || port > 0);
if (!a)
@@ -379,24 +378,32 @@ int avahi_send_dns_packet_ipv4(int fd, int interface, AvahiDnsPacket *p, const A
io.iov_base = AVAHI_DNS_PACKET_DATA(p);
io.iov_len = p->size;
- memset(cmsg_data, 0, sizeof(cmsg_data));
- cmsg = (struct cmsghdr*) cmsg_data;
- cmsg->cmsg_len = sizeof(cmsg_data);
- cmsg->cmsg_level = IPPROTO_IP;
- cmsg->cmsg_type = IP_PKTINFO;
-
- pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
- pkti->ipi_ifindex = interface;
-
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &io;
msg.msg_iovlen = 1;
- msg.msg_control = cmsg_data;
- msg.msg_controllen = sizeof(cmsg_data);
msg.msg_flags = 0;
+ if (interface >= 0) {
+ struct in_pktinfo *pkti;
+
+ memset(cmsg_data, 0, sizeof(cmsg_data));
+ cmsg = (struct cmsghdr*) cmsg_data;
+ cmsg->cmsg_len = sizeof(cmsg_data);
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+
+ pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
+ pkti->ipi_ifindex = interface;
+
+ msg.msg_control = cmsg_data;
+ msg.msg_controllen = sizeof(cmsg_data);
+ } else {
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ }
+
return sendmsg_loop(fd, &msg, 0);
}
@@ -405,12 +412,11 @@ int avahi_send_dns_packet_ipv6(int fd, int interface, AvahiDnsPacket *p, const A
struct msghdr msg;
struct iovec io;
struct cmsghdr *cmsg;
- struct in6_pktinfo *pkti;
uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
assert(fd >= 0);
assert(p);
- assert(avahi_dns_packet_is_valid(p) >= 0);
+ assert(avahi_dns_packet_check_valid(p) >= 0);
if (!a)
mdns_mcast_group_ipv6(&sa);
@@ -421,25 +427,34 @@ int avahi_send_dns_packet_ipv6(int fd, int interface, AvahiDnsPacket *p, const A
io.iov_base = AVAHI_DNS_PACKET_DATA(p);
io.iov_len = p->size;
- memset(cmsg_data, 0, sizeof(cmsg_data));
- cmsg = (struct cmsghdr*) cmsg_data;
- cmsg->cmsg_len = sizeof(cmsg_data);
- cmsg->cmsg_level = IPPROTO_IPV6;
- cmsg->cmsg_type = IPV6_PKTINFO;
-
- pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
- pkti->ipi6_ifindex = interface;
memset(&msg, 0, sizeof(msg));
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &io;
msg.msg_iovlen = 1;
- msg.msg_control = cmsg_data;
- msg.msg_controllen = sizeof(cmsg_data);
msg.msg_flags = 0;
- return sendmsg_loop(fd, &msg, 0 /*MSG_DONTROUTE*/);
+ if (interface >= 0) {
+ struct in6_pktinfo *pkti;
+
+ memset(cmsg_data, 0, sizeof(cmsg_data));
+ cmsg = (struct cmsghdr*) cmsg_data;
+ cmsg->cmsg_len = sizeof(cmsg_data);
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+
+ pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
+ pkti->ipi6_ifindex = interface;
+
+ msg.msg_control = cmsg_data;
+ msg.msg_controllen = sizeof(cmsg_data);
+ } else {
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ }
+
+ return sendmsg_loop(fd, &msg, 0);
}
AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, AvahiIPv4Address *ret_dest_address, int *ret_iface, uint8_t* ret_ttl) {
@@ -453,10 +468,6 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, A
int ms;
assert(fd >= 0);
- assert(ret_sa);
- assert(ret_dest_address);
- assert(ret_iface);
- assert(ret_ttl);
if (ioctl(fd, FIONREAD, &ms) < 0) {
avahi_log_warn("ioctl(): %s", strerror(errno));
@@ -469,8 +480,13 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, A
io.iov_len = p->max_size;
memset(&msg, 0, sizeof(msg));
- msg.msg_name = ret_sa;
- msg.msg_namelen = sizeof(struct sockaddr_in);
+ if (ret_sa) {
+ msg.msg_name = ret_sa;
+ msg.msg_namelen = sizeof(struct sockaddr_in);
+ } else {
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ }
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = aux;
@@ -482,7 +498,7 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, A
goto fail;
}
- if (ret_sa->sin_addr.s_addr == INADDR_ANY) {
+ if (ret_sa && ret_sa->sin_addr.s_addr == INADDR_ANY) {
/* Linux 2.4 behaves very strangely sometimes! */
/*avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), l); */
@@ -492,31 +508,31 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv4(int fd, struct sockaddr_in *ret_sa, A
assert(!(msg.msg_flags & MSG_CTRUNC));
assert(!(msg.msg_flags & MSG_TRUNC));
p->size = (size_t) l;
-
- *ret_ttl = 0;
-/* avahi_hexdump(msg.msg_control, msg.msg_controllen); */
-
+ if (ret_ttl)
+ *ret_ttl = 0;
+
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-/* avahi_hexdump(CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); */
-
if (cmsg->cmsg_level == SOL_IP) {
if (cmsg->cmsg_type == IP_TTL) {
- *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
+ if (ret_ttl)
+ *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
found_ttl = 1;
} else if (cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
- *ret_iface = (int) i->ipi_ifindex;
- ret_dest_address->address = i->ipi_addr.s_addr;
+
+ if (ret_iface)
+ *ret_iface = (int) i->ipi_ifindex;
+
+ if (ret_dest_address)
+ ret_dest_address->address = i->ipi_addr.s_addr;
found_iface = 1;
}
}
}
-/* avahi_log_debug("ttl=%u iface=%i", *ret_ttl, *ret_iface); */
-
assert(found_iface);
assert(found_ttl);
@@ -541,10 +557,6 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv6(int fd, struct sockaddr_in6 *ret_sa,
int found_ttl = 0, found_iface = 0;
assert(fd >= 0);
- assert(ret_sa);
- assert(ret_dest_address);
- assert(ret_iface);
- assert(ret_ttl);
if (ioctl(fd, FIONREAD, &ms) < 0) {
avahi_log_warn("ioctl(): %s", strerror(errno));
@@ -557,8 +569,14 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv6(int fd, struct sockaddr_in6 *ret_sa,
io.iov_len = p->max_size;
memset(&msg, 0, sizeof(msg));
- msg.msg_name = ret_sa;
- msg.msg_namelen = sizeof(struct sockaddr_in6);
+ if (ret_sa) {
+ msg.msg_name = ret_sa;
+ msg.msg_namelen = sizeof(struct sockaddr_in6);
+ } else {
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ }
+
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = aux;
@@ -571,19 +589,27 @@ AvahiDnsPacket* avahi_recv_dns_packet_ipv6(int fd, struct sockaddr_in6 *ret_sa,
}
p->size = (size_t) l;
-
- *ret_ttl = 0;
+
+ if (ret_ttl)
+ *ret_ttl = 0;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
- *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
+
+ if (ret_ttl)
+ *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
+
found_ttl = 1;
}
if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
- *ret_iface = i->ipi6_ifindex;
- memcpy(ret_dest_address->address, i->ipi6_addr.s6_addr, 16);
+
+ if (ret_iface)
+ *ret_iface = i->ipi6_ifindex;
+
+ if (ret_dest_address)
+ memcpy(ret_dest_address->address, i->ipi6_addr.s6_addr, 16);
found_iface = 1;
}
}
@@ -600,7 +626,7 @@ fail:
return NULL;
}
-int avahi_open_legacy_unicast_socket_ipv4(void) {
+int avahi_open_unicast_socket_ipv4(void) {
struct sockaddr_in local;
int fd = -1, yes;
@@ -648,8 +674,8 @@ fail:
return -1;
}
-int avahi_open_legacy_unicast_socket_ipv6(void) {
- struct sockaddr_in local;
+int avahi_open_unicast_socket_ipv6(void) {
+ struct sockaddr_in6 local;
int fd = -1, yes;
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
@@ -658,7 +684,7 @@ int avahi_open_legacy_unicast_socket_ipv6(void) {
}
memset(&local, 0, sizeof(local));
- local.sin_family = AF_INET;
+ local.sin6_family = AF_INET6;
if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
avahi_log_warn("bind() failed: %s\n", strerror(errno));
diff --git a/avahi-core/socket.h b/avahi-core/socket.h
index d795156..971e7e8 100644
--- a/avahi-core/socket.h
+++ b/avahi-core/socket.h
@@ -28,14 +28,15 @@
#include "dns.h"
#define AVAHI_MDNS_PORT 5353
+#define AVAHI_DNS_PORT 53
#define AVAHI_IPV4_MCAST_GROUP "224.0.0.251"
#define AVAHI_IPV6_MCAST_GROUP "ff02::fb"
int avahi_open_socket_ipv4(void);
int avahi_open_socket_ipv6(void);
-int avahi_open_legacy_unicast_socket_ipv4(void);
-int avahi_open_legacy_unicast_socket_ipv6(void);
+int avahi_open_unicast_socket_ipv4(void);
+int avahi_open_unicast_socket_ipv6(void);
int avahi_send_dns_packet_ipv4(int fd, int iface, AvahiDnsPacket *p, const AvahiIPv4Address *a, uint16_t port);
int avahi_send_dns_packet_ipv6(int fd, int iface, AvahiDnsPacket *p, const AvahiIPv6Address *a, uint16_t port);
diff --git a/avahi-core/wide-area.c b/avahi-core/wide-area.c
new file mode 100644
index 0000000..2f8661d
--- /dev/null
+++ b/avahi-core/wide-area.c
@@ -0,0 +1,693 @@
+/* $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
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <avahi-common/malloc.h>
+
+#include "server.h"
+#include "browse.h"
+#include "socket.h"
+#include "log.h"
+#include "hashmap.h"
+#include "wide-area.h"
+
+#define MAX_CACHE_ENTRIES 500
+
+typedef struct AvahiWideAreaCacheEntry AvahiWideAreaCacheEntry;
+
+struct AvahiWideAreaCacheEntry {
+ AvahiWideAreaLookupEngine *engine;
+
+ AvahiRecord *record;
+ struct timeval timestamp;
+ struct timeval expiry;
+
+ AvahiTimeEvent *time_event;
+
+ AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, by_key);
+ AVAHI_LLIST_FIELDS(AvahiWideAreaCacheEntry, cache);
+};
+
+struct AvahiWideAreaLookup {
+ AvahiWideAreaLookupEngine *engine;
+ int dead;
+
+ uint32_t id; /* effectively just an uint16_t, but we need it as an index for a hash table */
+ AvahiTimeEvent *time_event;
+
+ AvahiKey *key, *cname_key;
+
+ int n_send;
+ AvahiDnsPacket *packet;
+
+ AvahiWideAreaLookupCallback callback;
+ void *userdata;
+
+ AvahiAddress dns_server_used;
+
+ AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, lookups);
+ AVAHI_LLIST_FIELDS(AvahiWideAreaLookup, by_key);
+};
+
+struct AvahiWideAreaLookupEngine {
+ AvahiServer *server;
+
+ int fd_ipv4, fd_ipv6;
+ AvahiWatch *watch_ipv4, *watch_ipv6;
+
+ uint16_t next_id;
+
+ /* Cache */
+ AVAHI_LLIST_HEAD(AvahiWideAreaCacheEntry, cache);
+ AvahiHashmap *cache_by_key;
+ unsigned cache_n_entries;
+
+ /* Lookups */
+ AVAHI_LLIST_HEAD(AvahiWideAreaLookup, lookups);
+ AvahiHashmap *lookups_by_id;
+ AvahiHashmap *lookups_by_key;
+
+ int cleanup_dead;
+
+ AvahiAddress dns_servers[AVAHI_MAX_WIDE_AREA_SERVERS];
+ unsigned n_dns_servers;
+ unsigned current_dns_server;
+};
+
+static AvahiWideAreaLookup* find_lookup(AvahiWideAreaLookupEngine *e, uint16_t id) {
+ AvahiWideAreaLookup *l;
+ int i = (int) id;
+
+ assert(e);
+
+ if (!(l = avahi_hashmap_lookup(e->lookups_by_id, &i)))
+ return NULL;
+
+ assert(l->id == id);
+
+ if (l->dead)
+ return NULL;
+
+ return l;
+}
+
+static int send_to_dns_server(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p) {
+ AvahiAddress *a;
+
+ assert(e);
+ assert(p);
+
+ if (e->n_dns_servers <= 0)
+ return -1;
+
+ assert(e->current_dns_server < e->n_dns_servers);
+
+ a = &e->dns_servers[e->current_dns_server];
+
+ if (a->proto == AVAHI_PROTO_INET) {
+
+ if (e->fd_ipv4 < 0)
+ return -1;
+
+ return avahi_send_dns_packet_ipv4(e->fd_ipv4, AVAHI_IF_UNSPEC, p, &a->data.ipv4, AVAHI_DNS_PORT);
+
+ } else {
+ assert(a->proto == AVAHI_PROTO_INET6);
+
+ if (e->fd_ipv6 < 0)
+ return -1;
+
+ return avahi_send_dns_packet_ipv6(e->fd_ipv6, AVAHI_IF_UNSPEC, p, &a->data.ipv6, AVAHI_DNS_PORT);
+ }
+}
+
+static void next_dns_server(AvahiWideAreaLookupEngine *e) {
+ assert(e);
+
+ e->current_dns_server++;
+
+ if (e->current_dns_server >= e->n_dns_servers)
+ e->current_dns_server = 0;
+}
+
+static void sender_timeout_callback(AvahiTimeEvent *e, void *userdata) {
+ AvahiWideAreaLookup *l = userdata;
+ struct timeval tv;
+
+ assert(l);
+
+ /* Try another DNS server after three retries */
+ if (l->n_send >= 3 && avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0) {
+ next_dns_server(l->engine);
+
+ if (avahi_address_cmp(&l->engine->dns_servers[l->engine->current_dns_server], &l->dns_server_used) == 0)
+ /* There is no other DNS server, fail */
+ l->n_send = 1000;
+ }
+
+ if (l->n_send >= 6) {
+ avahi_log_warn(__FILE__": Query timed out.");
+ l->callback(l->engine, AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, NULL, l->userdata);
+ avahi_wide_area_lookup_free(l);
+ return;
+ }
+
+ assert(l->packet);
+ send_to_dns_server(l->engine, l->packet);
+ l->n_send++;
+
+ avahi_time_event_update(e, avahi_elapse_time(&tv, 1000, 0));
+}
+
+AvahiWideAreaLookup *avahi_wide_area_lookup_new(
+ AvahiWideAreaLookupEngine *e,
+ AvahiKey *key,
+ AvahiWideAreaLookupCallback callback,
+ void *userdata) {
+
+ struct timeval tv;
+ AvahiWideAreaLookup *l, *t;
+ uint8_t *p;
+
+ assert(e);
+ assert(key);
+ assert(callback);
+ assert(userdata);
+
+ l = avahi_new(AvahiWideAreaLookup, 1);
+ l->engine = e;
+ l->dead = 0;
+ l->key = avahi_key_ref(key);
+ l->cname_key = avahi_key_new_cname(l->key);
+ l->callback = callback;
+ l->userdata = userdata;
+
+ /* If more than 65K wide area quries are issued simultaneously,
+ * this will break. This should be limited by some higher level */
+
+ for (;; e->next_id++)
+ if (!find_lookup(e, e->next_id))
+ break; /* This ID is not yet used. */
+
+ l->id = e->next_id++;
+
+ /* We keep the packet around in case we need to repeat our query */
+ l->packet = avahi_dns_packet_new(0);
+
+ avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_ID, (uint16_t) l->id);
+ avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 1, 0, 0, 0, 0, 0));
+
+ p = avahi_dns_packet_append_key(l->packet, key, 0);
+ assert(p);
+
+ avahi_dns_packet_set_field(l->packet, AVAHI_DNS_FIELD_QDCOUNT, 1);
+
+ if (send_to_dns_server(e, l->packet) < 0) {
+ avahi_log_error(__FILE__": Failed to send packet.");
+ avahi_dns_packet_free(l->packet);
+ avahi_key_unref(l->key);
+ if (l->cname_key)
+ avahi_key_unref(l->cname_key);
+ avahi_free(l);
+ return NULL;
+ }
+
+ l->n_send = 1;
+
+ l->time_event = avahi_time_event_new(e->server->time_event_queue, avahi_elapse_time(&tv, 500, 0), sender_timeout_callback, l);
+
+ avahi_hashmap_insert(e->lookups_by_id, &l->id, l);
+
+ t = avahi_hashmap_lookup(e->lookups_by_key, l->key);
+ AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, by_key, t, l);
+ avahi_hashmap_replace(e->lookups_by_key, avahi_key_ref(l->key), t);
+
+ AVAHI_LLIST_PREPEND(AvahiWideAreaLookup, lookups, e->lookups, l);
+
+ return l;
+}
+
+static void lookup_stop(AvahiWideAreaLookup *l) {
+ assert(l);
+
+ l->callback = NULL;
+
+ if (l->time_event) {
+ avahi_time_event_free(l->time_event);
+ l->time_event = NULL;
+ }
+}
+
+static void lookup_destroy(AvahiWideAreaLookup *l) {
+ AvahiWideAreaLookup *t;
+ assert(l);
+
+ lookup_stop(l);
+
+ t = avahi_hashmap_lookup(l->engine->lookups_by_key, l->key);
+ AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, by_key, t, l);
+ if (t)
+ avahi_hashmap_replace(l->engine->lookups_by_key, avahi_key_ref(l->key), t);
+ else
+ avahi_hashmap_remove(l->engine->lookups_by_key, l->key);
+
+ AVAHI_LLIST_REMOVE(AvahiWideAreaLookup, lookups, l->engine->lookups, l);
+
+ avahi_hashmap_remove(l->engine->lookups_by_id, &l->id);
+ avahi_dns_packet_free(l->packet);
+
+ if (l->key)
+ avahi_key_unref(l->key);
+
+ if (l->cname_key)
+ avahi_key_unref(l->cname_key);
+
+ avahi_free(l);
+}
+
+void avahi_wide_area_lookup_free(AvahiWideAreaLookup *l) {
+ assert(l);
+
+ if (l->dead)
+ return;
+
+ l->dead = 1;
+ l->engine->cleanup_dead = 1;
+ lookup_stop(l);
+}
+
+void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e) {
+ AvahiWideAreaLookup *l, *n;
+ assert(e);
+
+ while (e->cleanup_dead) {
+ e->cleanup_dead = 0;
+
+ for (l = e->lookups; l; l = n) {
+ n = l->lookups_next;
+
+ if (l->dead)
+ lookup_destroy(l);
+ }
+ }
+}
+
+static void cache_entry_free(AvahiWideAreaCacheEntry *c) {
+ AvahiWideAreaCacheEntry *t;
+ assert(c);
+
+ if (c->time_event)
+ avahi_time_event_free(c->time_event);
+
+ AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, cache, c->engine->cache, c);
+
+ t = avahi_hashmap_lookup(c->engine->cache_by_key, c->record->key);
+ AVAHI_LLIST_REMOVE(AvahiWideAreaCacheEntry, by_key, t, c);
+ if (t)
+ avahi_hashmap_replace(c->engine->cache_by_key, avahi_key_ref(c->record->key), t);
+ else
+ avahi_hashmap_remove(c->engine->cache_by_key, c->record->key);
+
+ c->engine->cache_n_entries --;
+
+ avahi_record_unref(c->record);
+ avahi_free(c);
+}
+
+static void expiry_event(AvahiTimeEvent *te, void *userdata) {
+ AvahiWideAreaCacheEntry *e = userdata;
+
+ assert(te);
+ assert(e);
+
+ cache_entry_free(e);
+}
+
+static AvahiWideAreaCacheEntry* find_record_in_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) {
+ AvahiWideAreaCacheEntry *c;
+
+ assert(e);
+ assert(r);
+
+ for (c = avahi_hashmap_lookup(e->cache_by_key, r->key); c; c = c->by_key_next)
+ if (avahi_record_equal_no_ttl(r, c->record))
+ return c;
+
+ return NULL;
+}
+
+static void run_callbacks(AvahiWideAreaLookupEngine *e, AvahiRecord *r) {
+ AvahiWideAreaLookup *l;
+
+ assert(e);
+ assert(r);
+
+ for (l = avahi_hashmap_lookup(e->lookups_by_key, r->key); l; l = l->by_key_next) {
+ if (l->dead || !l->callback)
+ continue;
+
+ l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, r, l->userdata);
+ }
+
+ if (r->key->clazz == AVAHI_DNS_CLASS_IN && r->key->type == AVAHI_DNS_TYPE_CNAME) {
+ /* It's a CNAME record, so we have to scan the all lookups to see if one matches */
+
+ for (l = e->lookups; l; l = l->lookups_next) {
+ AvahiKey *key;
+
+ if (l->dead || !l->callback)
+ continue;
+
+ if ((key = avahi_key_new_cname(l->key))) {
+ if (avahi_key_equal(r->key, key))
+ l->callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, r, l->userdata);
+
+ avahi_key_unref(key);
+ }
+ }
+ }
+}
+
+static void add_to_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) {
+ AvahiWideAreaCacheEntry *c;
+ int is_new;
+
+ assert(e);
+ assert(r);
+
+ if ((c = find_record_in_cache(e, r))) {
+ is_new = 0;
+
+ /* Update the existing entry */
+ avahi_record_unref(c->record);
+ } else {
+ AvahiWideAreaCacheEntry *t;
+
+ is_new = 1;
+
+ /* Enforce cache size */
+ if (e->cache_n_entries >= MAX_CACHE_ENTRIES)
+ /* Eventually we should improve the caching algorithm here */
+ goto finish;
+
+ c = avahi_new(AvahiWideAreaCacheEntry, 1);
+ c->engine = e;
+ c->time_event = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, cache, e->cache, c);
+
+ /* Add the new entry to the cache entry hash table */
+ t = avahi_hashmap_lookup(e->cache_by_key, r->key);
+ AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, by_key, t, c);
+ avahi_hashmap_replace(e->cache_by_key, avahi_key_ref(r->key), t);
+
+ e->cache_n_entries ++;
+ }
+
+ c->record = avahi_record_ref(r);
+
+ gettimeofday(&c->timestamp, NULL);
+ c->expiry = c->timestamp;
+ avahi_timeval_add(&c->expiry, r->ttl * 1000000);
+
+ if (c->time_event)
+ avahi_time_event_update(c->time_event, &c->expiry);
+ else
+ c->time_event = avahi_time_event_new(e->server->time_event_queue, &c->expiry, expiry_event, c);
+
+finish:
+
+ if (is_new)
+ run_callbacks(e, r);
+}
+
+static void handle_packet(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p, AvahiAddress *a) {
+ AvahiWideAreaLookup *l = NULL;
+ int i, r;
+
+ assert(e);
+ assert(p);
+
+ if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
+ avahi_log_warn(__FILE__": Ignoring invalid response for wide area datagram.");
+ goto finish;
+ }
+
+/* avahi_log_debug(__FILE__": Recieving unicast packet %u", avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)); */
+
+ if (!(l = find_lookup(e, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
+/* avahi_log_warn(__FILE__": Got response for query we didn't send."); */
+ goto finish;
+ }
+
+
+ if ((r = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & 15) != 0) {
+/* avahi_log_debug(__FILE__": Response failed for wide area datagram: %i", r); */
+
+ /* Tell the user about the failure */
+ if (l->callback)
+ l->callback(e, r == 3 ? AVAHI_BROWSER_NOT_FOUND : AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, NULL, l->userdata);
+ goto finish;
+ }
+
+ /* Skip over the question */
+ for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); i > 0; i--) {
+ AvahiKey *k;
+
+ if (!(k = avahi_dns_packet_consume_key(p, NULL))) {
+ avahi_log_warn(__FILE__": Wide area response packet too short.");
+ goto finish;
+ }
+
+ avahi_key_unref(k);
+ }
+
+ for (i = (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
+ (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) +
+ (int) avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); i > 0; i--) {
+
+ AvahiRecord *rr;
+
+ if (!(rr = avahi_dns_packet_consume_record(p, NULL))) {
+ avahi_log_warn(__FILE__": Wide area response packet too short (2).");
+ goto finish;
+ }
+
+ add_to_cache(e, rr);
+
+ avahi_record_unref(rr);
+ }
+
+ /** Inform the user that this is the last reply */
+ if (l->callback && !l->dead)
+ l->callback(e, AVAHI_BROWSER_ALL_FOR_NOW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA, NULL, l->userdata);
+
+finish:
+ if (l)
+ lookup_stop(l);
+}
+
+static void socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
+ AvahiWideAreaLookupEngine *e = userdata;
+ AvahiAddress a;
+ AvahiDnsPacket *p = NULL;
+
+ if (fd == e->fd_ipv4) {
+ struct sockaddr_in sa;
+
+ if ((p = avahi_recv_dns_packet_ipv4(e->fd_ipv4, &sa, NULL, NULL, NULL)))
+ avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
+
+ } else if (fd == e->fd_ipv6) {
+ struct sockaddr_in6 sa6;
+
+ if ((p = avahi_recv_dns_packet_ipv6(e->fd_ipv6, &sa6, NULL, NULL, NULL)))
+ avahi_address_from_sockaddr((struct sockaddr*) &sa6, &a);
+
+ }
+
+ if (p) {
+ handle_packet(e, p, &a);
+ avahi_dns_packet_free(p);
+ }
+}
+
+AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s) {
+ AvahiWideAreaLookupEngine *e;
+
+ assert(s);
+
+ e = avahi_new(AvahiWideAreaLookupEngine, 1);
+ e->server = s;
+ e->cleanup_dead = 0;
+
+ /* Create sockets */
+ e->fd_ipv4 = avahi_open_unicast_socket_ipv4();
+ e->fd_ipv6 = avahi_open_unicast_socket_ipv6();
+
+ if (e->fd_ipv4 < 0 && e->fd_ipv6 < 0) {
+ avahi_log_error(__FILE__": Failed to create wide area sockets: %s\n", strerror(errno));
+
+ if (e->fd_ipv6 >= 0)
+ close(e->fd_ipv6);
+
+ if (e->fd_ipv4 >= 0)
+ close(e->fd_ipv4);
+
+ avahi_free(e);
+ return NULL;
+ }
+
+ /* Create watches */
+ if (e->fd_ipv4 >= 0)
+ e->watch_ipv4 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv4, AVAHI_WATCH_IN, socket_event, e);
+ if (e->fd_ipv6 >= 0)
+ e->watch_ipv6 = s->poll_api->watch_new(e->server->poll_api, e->fd_ipv6, AVAHI_WATCH_IN, socket_event, e);
+
+ e->n_dns_servers = e->current_dns_server = 0;
+ e->next_id = (uint16_t) rand();
+
+ /* Initialize cache */
+ AVAHI_LLIST_HEAD_INIT(AvahiWideAreaCacheEntry, e->cache);
+ e->cache_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL);
+ e->cache_n_entries = 0;
+
+ /* Initialize lookup list */
+ e->lookups_by_id = avahi_hashmap_new((AvahiHashFunc) avahi_int_hash, (AvahiEqualFunc) avahi_int_equal, NULL, NULL);
+ e->lookups_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, (AvahiFreeFunc) avahi_key_unref, NULL);
+ AVAHI_LLIST_HEAD_INIT(AvahiWideAreaLookup, e->lookups);
+
+ return e;
+}
+
+void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e) {
+ assert(e);
+
+ avahi_wide_area_clear_cache(e);
+
+ while (e->lookups)
+ lookup_destroy(e->lookups);
+
+ avahi_hashmap_free(e->cache_by_key);
+ avahi_hashmap_free(e->lookups_by_id);
+ avahi_hashmap_free(e->lookups_by_key);
+
+ if (e->watch_ipv4)
+ e->server->poll_api->watch_free(e->watch_ipv4);
+
+ if (e->watch_ipv6)
+ e->server->poll_api->watch_free(e->watch_ipv6);
+
+ if (e->fd_ipv6 >= 0)
+ close(e->fd_ipv6);
+
+ if (e->fd_ipv4 >= 0)
+ close(e->fd_ipv4);
+
+ avahi_free(e);
+}
+
+void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e) {
+ assert(e);
+
+ while (e->cache)
+ cache_entry_free(e->cache);
+
+ assert(e->cache_n_entries == 0);
+}
+
+
+void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n) {
+ assert(e);
+
+
+ if (a) {
+ for (e->n_dns_servers = 0; n > 0 && e->n_dns_servers < AVAHI_MAX_WIDE_AREA_SERVERS; a++, n--)
+ if ((a->proto == AVAHI_PROTO_INET && e->fd_ipv4 >= 0) || (a->proto == AVAHI_PROTO_INET6 && e->fd_ipv6 >= 0))
+ e->dns_servers[e->n_dns_servers++] = *a;
+ } else {
+ assert(n == 0);
+ e->n_dns_servers = 0;
+ }
+
+ e->current_dns_server = 0;
+
+ avahi_wide_area_clear_cache(e);
+}
+
+void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata) {
+ AvahiWideAreaCacheEntry *c;
+
+ assert(e);
+ assert(callback);
+
+ callback(";; WIDE AREA CACHE ;;; ", userdata);
+
+ for (c = e->cache; c; c = c->cache_next) {
+ char *t = avahi_record_to_string(c->record);
+ callback(t, userdata);
+ avahi_free(t);
+ }
+}
+
+unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata) {
+ AvahiWideAreaCacheEntry *c;
+ AvahiKey *cname_key;
+ unsigned n = 0;
+
+ assert(e);
+ assert(key);
+ assert(callback);
+
+ for (c = avahi_hashmap_lookup(e->cache_by_key, key); c; c = c->by_key_next) {
+ callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA|AVAHI_LOOKUP_CALLBACK_CACHED, c->record, userdata);
+ n++;
+ }
+
+ if ((cname_key = avahi_key_new_cname(key))) {
+
+ for (c = avahi_hashmap_lookup(e->cache_by_key, cname_key); c; c = c->by_key_next) {
+ callback(e, AVAHI_BROWSER_NEW, AVAHI_LOOKUP_CALLBACK_WIDE_AREA|AVAHI_LOOKUP_CALLBACK_CACHED, c->record, userdata);
+ n++;
+ }
+
+ avahi_key_unref(cname_key);
+ }
+
+ return n;
+}
+
+int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e) {
+ assert(e);
+
+ return e->n_dns_servers > 0;
+}
+
+
+
diff --git a/avahi-core/wide-area.h b/avahi-core/wide-area.h
new file mode 100644
index 0000000..1af613b
--- /dev/null
+++ b/avahi-core/wide-area.h
@@ -0,0 +1,54 @@
+#ifndef foowideareahfoo
+#define foowideareahfoo
+
+/* $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
+ USA.
+***/
+
+#include "lookup.h"
+#include "browse.h"
+
+typedef struct AvahiWideAreaLookupEngine AvahiWideAreaLookupEngine;
+typedef struct AvahiWideAreaLookup AvahiWideAreaLookup;
+
+typedef void (*AvahiWideAreaLookupCallback)(
+ AvahiWideAreaLookupEngine *e,
+ AvahiBrowserEvent event,
+ AvahiLookupResultFlags flags,
+ AvahiRecord *r,
+ void *userdata);
+
+AvahiWideAreaLookupEngine *avahi_wide_area_engine_new(AvahiServer *s);
+void avahi_wide_area_engine_free(AvahiWideAreaLookupEngine *e);
+
+unsigned avahi_wide_area_scan_cache(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata);
+void avahi_wide_area_cache_dump(AvahiWideAreaLookupEngine *e, AvahiDumpCallback callback, void* userdata);
+void avahi_wide_area_set_servers(AvahiWideAreaLookupEngine *e, const AvahiAddress *a, unsigned n);
+void avahi_wide_area_clear_cache(AvahiWideAreaLookupEngine *e);
+void avahi_wide_area_cleanup(AvahiWideAreaLookupEngine *e);
+int avahi_wide_area_has_servers(AvahiWideAreaLookupEngine *e);
+
+AvahiWideAreaLookup *avahi_wide_area_lookup_new(AvahiWideAreaLookupEngine *e, AvahiKey *key, AvahiWideAreaLookupCallback callback, void *userdata);
+void avahi_wide_area_lookup_free(AvahiWideAreaLookup *q);
+
+
+
+#endif
+