summaryrefslogtreecommitdiffstats
path: root/trunk/avahi-core
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/avahi-core')
-rw-r--r--trunk/avahi-core/Makefile.am160
-rw-r--r--trunk/avahi-core/addr-util.c79
-rw-r--r--trunk/avahi-core/addr-util.h45
-rw-r--r--trunk/avahi-core/announce.c526
-rw-r--r--trunk/avahi-core/announce.h71
-rw-r--r--trunk/avahi-core/avahi-reflector.c63
-rw-r--r--trunk/avahi-core/avahi-test.c403
-rw-r--r--trunk/avahi-core/browse-dns-server.c324
-rw-r--r--trunk/avahi-core/browse-domain.c237
-rw-r--r--trunk/avahi-core/browse-service-type.c159
-rw-r--r--trunk/avahi-core/browse-service.c169
-rw-r--r--trunk/avahi-core/browse.c615
-rw-r--r--trunk/avahi-core/browse.h62
-rw-r--r--trunk/avahi-core/cache.c508
-rw-r--r--trunk/avahi-core/cache.h101
-rw-r--r--trunk/avahi-core/conformance-test.c160
-rw-r--r--trunk/avahi-core/core.h153
-rw-r--r--trunk/avahi-core/dns-srv-rr.h89
-rw-r--r--trunk/avahi-core/dns-test.c115
-rw-r--r--trunk/avahi-core/dns.c856
-rw-r--r--trunk/avahi-core/dns.h112
-rw-r--r--trunk/avahi-core/domain-util.c135
-rw-r--r--trunk/avahi-core/domain-util.h47
-rw-r--r--trunk/avahi-core/entry.c1206
-rw-r--r--trunk/avahi-core/fdutil.c73
-rw-r--r--trunk/avahi-core/fdutil.h35
-rwxr-xr-xtrunk/avahi-core/findstatic.pl70
-rw-r--r--trunk/avahi-core/hashmap-test.c64
-rw-r--r--trunk/avahi-core/hashmap.c250
-rw-r--r--trunk/avahi-core/hashmap.h55
-rw-r--r--trunk/avahi-core/iface-linux.c371
-rw-r--r--trunk/avahi-core/iface-linux.h42
-rw-r--r--trunk/avahi-core/iface-pfroute.c521
-rw-r--r--trunk/avahi-core/iface-pfroute.h39
-rw-r--r--trunk/avahi-core/iface.c812
-rw-r--r--trunk/avahi-core/iface.h192
-rw-r--r--trunk/avahi-core/internal.h227
-rw-r--r--trunk/avahi-core/log.c88
-rw-r--r--trunk/avahi-core/log.h75
-rw-r--r--trunk/avahi-core/lookup.h237
-rw-r--r--trunk/avahi-core/multicast-lookup.c352
-rw-r--r--trunk/avahi-core/multicast-lookup.h53
-rw-r--r--trunk/avahi-core/netlink.c211
-rw-r--r--trunk/avahi-core/netlink.h43
-rw-r--r--trunk/avahi-core/prioq-test.c122
-rw-r--r--trunk/avahi-core/prioq.c390
-rw-r--r--trunk/avahi-core/prioq.h51
-rw-r--r--trunk/avahi-core/probe-sched.c402
-rw-r--r--trunk/avahi-core/probe-sched.h36
-rw-r--r--trunk/avahi-core/publish.h177
-rw-r--r--trunk/avahi-core/querier-test.c124
-rw-r--r--trunk/avahi-core/querier.c271
-rw-r--r--trunk/avahi-core/querier.h50
-rw-r--r--trunk/avahi-core/query-sched.c452
-rw-r--r--trunk/avahi-core/query-sched.h38
-rw-r--r--trunk/avahi-core/resolve-address.c270
-rw-r--r--trunk/avahi-core/resolve-host-name.c299
-rw-r--r--trunk/avahi-core/resolve-service.c491
-rw-r--r--trunk/avahi-core/response-sched.c516
-rw-r--r--trunk/avahi-core/response-sched.h39
-rw-r--r--trunk/avahi-core/rr-util.h64
-rw-r--r--trunk/avahi-core/rr.c703
-rw-r--r--trunk/avahi-core/rr.h174
-rw-r--r--trunk/avahi-core/rrlist.c190
-rw-r--r--trunk/avahi-core/rrlist.h42
-rw-r--r--trunk/avahi-core/server.c1723
-rw-r--r--trunk/avahi-core/socket.c930
-rw-r--r--trunk/avahi-core/socket.h49
-rw-r--r--trunk/avahi-core/timeeventq-test.c69
-rw-r--r--trunk/avahi-core/timeeventq.c227
-rw-r--r--trunk/avahi-core/timeeventq.h48
-rw-r--r--trunk/avahi-core/update-test.c92
-rw-r--r--trunk/avahi-core/util.c122
-rw-r--r--trunk/avahi-core/util.h43
-rw-r--r--trunk/avahi-core/wide-area.c725
-rw-r--r--trunk/avahi-core/wide-area.h54
76 files changed, 19188 insertions, 0 deletions
diff --git a/trunk/avahi-core/Makefile.am b/trunk/avahi-core/Makefile.am
new file mode 100644
index 0000000..cb2d8f9
--- /dev/null
+++ b/trunk/avahi-core/Makefile.am
@@ -0,0 +1,160 @@
+# $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 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 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.
+
+AM_CFLAGS=-I$(top_srcdir)
+
+# This cool debug trap works on i386/gcc only
+AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")'
+
+avahiincludedir=$(includedir)/avahi-core
+
+avahiinclude_HEADERS = \
+ core.h \
+ log.h \
+ rr.h \
+ publish.h \
+ lookup.h
+
+lib_LTLIBRARIES = \
+ libavahi-core.la
+
+if ENABLE_TESTS
+noinst_PROGRAMS = \
+ prioq-test \
+ avahi-test \
+ conformance-test \
+ avahi-reflector \
+ dns-test \
+ timeeventq-test \
+ hashmap-test \
+ querier-test \
+ update-test
+endif
+
+libavahi_core_la_SOURCES = \
+ timeeventq.c timeeventq.h\
+ iface.c iface.h \
+ server.c internal.h entry.c \
+ prioq.c prioq.h \
+ cache.c cache.h \
+ socket.c socket.h \
+ response-sched.c response-sched.h \
+ query-sched.c query-sched.h \
+ probe-sched.c probe-sched.h \
+ announce.c announce.h \
+ browse.c browse.h \
+ rrlist.c rrlist.h \
+ resolve-host-name.c \
+ resolve-address.c \
+ browse-domain.c \
+ browse-service-type.c \
+ browse-service.c \
+ resolve-service.c \
+ dns.c dns.h \
+ rr.c rr.h rr-util.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 \
+ wide-area.c wide-area.h \
+ multicast-lookup.c multicast-lookup.h \
+ querier.c querier.h \
+ addr-util.h addr-util.c \
+ domain-util.h domain-util.c \
+ dns-srv-rr.h
+
+if HAVE_NETLINK
+libavahi_core_la_SOURCES += \
+ iface-linux.c iface-linux.h \
+ netlink.c netlink.h
+else
+if HAVE_PF_ROUTE
+libavahi_core_la_SOURCES += \
+ iface-pfroute.c iface-pfroute.h
+endif
+endif
+
+libavahi_core_la_CFLAGS = $(AM_CFLAGS)
+libavahi_core_la_LIBADD = $(AM_LDADD) ../avahi-common/libavahi-common.la
+libavahi_core_la_LDFLAGS = $(AM_LDFLAGS) -export-dynamic -version-info $(LIBAVAHI_CORE_VERSION_INFO)
+
+prioq_test_SOURCES = \
+ prioq-test.c \
+ prioq.c prioq.h
+prioq_test_CFLAGS = $(AM_CFLAGS)
+prioq_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la
+
+avahi_test_SOURCES = \
+ avahi-test.c
+avahi_test_CFLAGS = $(AM_CFLAGS)
+avahi_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la
+
+update_test_SOURCES = \
+ update-test.c
+update_test_CFLAGS = $(AM_CFLAGS)
+update_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)
+conformance_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la
+
+avahi_reflector_SOURCES = \
+ avahi-reflector.c
+avahi_reflector_CFLAGS = $(AM_CFLAGS)
+avahi_reflector_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la libavahi-core.la
+
+dns_test_SOURCES = \
+ dns.c dns.h \
+ dns-test.c \
+ log.c log.h \
+ util.c util.h \
+ rr.c rr.h \
+ hashmap.c hashmap.h \
+ domain-util.c domain-util.h
+dns_test_CFLAGS = $(AM_CFLAGS)
+dns_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la
+
+timeeventq_test_SOURCES = \
+ timeeventq-test.c \
+ timeeventq.h timeeventq.c \
+ prioq.h prioq.c \
+ log.c log.h
+timeeventq_test_CFLAGS = $(AM_CFLAGS)
+timeeventq_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la
+
+hashmap_test_SOURCES = \
+ hashmap-test.c \
+ hashmap.h hashmap.c \
+ util.h util.c
+hashmap_test_CFLAGS = $(AM_CFLAGS)
+hashmap_test_LDADD = $(AM_LDADD) ../avahi-common/libavahi-common.la
+
+valgrind: avahi-test
+ libtool --mode=execute valgrind ./avahi-test
+
+gdb: avahi-test
+ libtool --mode=execute gdb ./avahi-test
diff --git a/trunk/avahi-core/addr-util.c b/trunk/avahi-core/addr-util.c
new file mode 100644
index 0000000..9e2d1e9
--- /dev/null
+++ b/trunk/avahi-core/addr-util.c
@@ -0,0 +1,79 @@
+/* $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 <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <assert.h>
+
+#include "addr-util.h"
+
+AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr) {
+ assert(sa);
+ assert(ret_addr);
+
+ assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+
+ ret_addr->proto = avahi_af_to_proto(sa->sa_family);
+
+ if (sa->sa_family == AF_INET)
+ memcpy(&ret_addr->data.ipv4, &((const struct sockaddr_in*) sa)->sin_addr, sizeof(ret_addr->data.ipv4));
+ else
+ memcpy(&ret_addr->data.ipv6, &((const struct sockaddr_in6*) sa)->sin6_addr, sizeof(ret_addr->data.ipv6));
+
+ return ret_addr;
+}
+
+uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa) {
+ assert(sa);
+
+ assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+
+ if (sa->sa_family == AF_INET)
+ return ntohs(((const struct sockaddr_in*) sa)->sin_port);
+ else
+ return ntohs(((const struct sockaddr_in6*) sa)->sin6_port);
+}
+
+int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a) {
+
+ static const uint8_t ipv4_in_ipv6[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF
+ };
+
+ assert(a);
+
+ if (a->proto != AVAHI_PROTO_INET6)
+ return 0;
+
+ return memcmp(a->data.ipv6.address, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0;
+}
+
+
+
diff --git a/trunk/avahi-core/addr-util.h b/trunk/avahi-core/addr-util.h
new file mode 100644
index 0000000..4134de1
--- /dev/null
+++ b/trunk/avahi-core/addr-util.h
@@ -0,0 +1,45 @@
+#ifndef fooaddrutilhfoo
+#define fooaddrutilhfoo
+
+/* $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 <inttypes.h>
+#include <sys/socket.h>
+
+#include <avahi-common/cdecl.h>
+#include <avahi-common/address.h>
+
+AVAHI_C_DECL_BEGIN
+
+/** Make an address structture of a sockaddr structure */
+AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr);
+
+/** Return the port number of a sockaddr structure (either IPv4 or IPv6) */
+uint16_t avahi_port_from_sockaddr(const struct sockaddr* sa);
+
+/** Check whether the specified IPv6 address is in fact an
+ * encapsulated IPv4 address, returns 1 if yes, 0 otherwise */
+int avahi_address_is_ipv4_in_ipv6(const AvahiAddress *a);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/announce.c b/trunk/avahi-core/announce.c
new file mode 100644
index 0000000..4a87e1d
--- /dev/null
+++ b/trunk/avahi-core/announce.c
@@ -0,0 +1,526 @@
+/* $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 <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+
+#include "announce.h"
+#include "log.h"
+#include "rr-util.h"
+
+#define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250
+#define AVAHI_PROBE_JITTER_MSEC 250
+#define AVAHI_PROBE_INTERVAL_MSEC 250
+
+static void remove_announcer(AvahiServer *s, AvahiAnnouncer *a) {
+ assert(s);
+ assert(a);
+
+ if (a->time_event)
+ avahi_time_event_free(a->time_event);
+
+ AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_interface, a->interface->announcers, a);
+ AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_entry, a->entry->announcers, a);
+
+ avahi_free(a);
+}
+
+static void elapse_announce(AvahiTimeEvent *e, void *userdata);
+
+static void set_timeout(AvahiAnnouncer *a, const struct timeval *tv) {
+ assert(a);
+
+ if (!tv) {
+ if (a->time_event) {
+ avahi_time_event_free(a->time_event);
+ a->time_event = NULL;
+ }
+ } else {
+
+ if (a->time_event)
+ avahi_time_event_update(a->time_event, tv);
+ else
+ a->time_event = avahi_time_event_new(a->server->time_event_queue, tv, elapse_announce, a);
+ }
+}
+
+static void next_state(AvahiAnnouncer *a);
+
+void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately) {
+ AvahiEntry *e;
+ assert(g);
+ assert(!g->dead);
+
+ /* Check whether all group members have been probed */
+
+ if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0)
+ return;
+
+ avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED);
+
+ if (g->dead)
+ return;
+
+ for (e = g->entries; e; e = e->by_group_next) {
+ AvahiAnnouncer *a;
+
+ for (a = e->announcers; a; a = a->by_entry_next) {
+
+ if (a->state != AVAHI_WAITING)
+ continue;
+
+ a->state = AVAHI_ANNOUNCING;
+
+ if (immediately) {
+ /* Shortcut */
+
+ a->n_iteration = 1;
+ next_state(a);
+ } else {
+ struct timeval tv;
+ a->n_iteration = 0;
+ avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
+ set_timeout(a, &tv);
+ }
+ }
+ }
+}
+
+static void next_state(AvahiAnnouncer *a) {
+ assert(a);
+
+ if (a->state == AVAHI_WAITING) {
+
+ assert(a->entry->group);
+
+ avahi_s_entry_group_check_probed(a->entry->group, 1);
+
+ } else if (a->state == AVAHI_PROBING) {
+
+ if (a->n_iteration >= 4) {
+ /* Probing done */
+
+ if (a->entry->group) {
+ assert(a->entry->group->n_probing);
+ a->entry->group->n_probing--;
+ }
+
+ if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING)
+ a->state = AVAHI_WAITING;
+ else {
+ a->state = AVAHI_ANNOUNCING;
+ a->n_iteration = 1;
+ }
+
+ set_timeout(a, NULL);
+ next_state(a);
+ } else {
+ struct timeval tv;
+
+ avahi_interface_post_probe(a->interface, a->entry->record, 0);
+
+ avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0);
+ set_timeout(a, &tv);
+
+ a->n_iteration++;
+ }
+
+ } else if (a->state == AVAHI_ANNOUNCING) {
+
+ if (a->entry->flags & AVAHI_PUBLISH_UNIQUE)
+ /* Send the whole rrset at once */
+ avahi_server_prepare_matching_responses(a->server, a->interface, a->entry->record->key, 0);
+ else
+ avahi_server_prepare_response(a->server, a->interface, a->entry, 0, 0);
+
+ avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, 0, 0);
+
+ if (++a->n_iteration >= 4) {
+ /* Announcing done */
+
+ a->state = AVAHI_ESTABLISHED;
+
+ set_timeout(a, NULL);
+ } else {
+ struct timeval tv;
+ avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
+
+ if (a->n_iteration < 10)
+ a->sec_delay *= 2;
+
+ set_timeout(a, &tv);
+ }
+ }
+}
+
+static void elapse_announce(AvahiTimeEvent *e, void *userdata) {
+ assert(e);
+
+ next_state(userdata);
+}
+
+static AvahiAnnouncer *get_announcer(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
+ AvahiAnnouncer *a;
+
+ assert(s);
+ assert(e);
+ assert(i);
+
+ for (a = e->announcers; a; a = a->by_entry_next)
+ if (a->interface == i)
+ return a;
+
+ return NULL;
+}
+
+static void go_to_initial_state(AvahiAnnouncer *a) {
+ AvahiEntry *e;
+ struct timeval tv;
+
+ assert(a);
+ e = a->entry;
+
+ if ((e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE))
+ a->state = AVAHI_PROBING;
+ else if (!(e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)) {
+
+ if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED)
+ a->state = AVAHI_ANNOUNCING;
+ else
+ a->state = AVAHI_WAITING;
+
+ } else
+ a->state = AVAHI_ESTABLISHED;
+
+ a->n_iteration = 1;
+ a->sec_delay = 1;
+
+ if (a->state == AVAHI_PROBING && e->group)
+ e->group->n_probing++;
+
+ if (a->state == AVAHI_PROBING)
+ set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC));
+ else if (a->state == AVAHI_ANNOUNCING)
+ set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC));
+ else
+ set_timeout(a, NULL);
+}
+
+static void new_announcer(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) {
+ AvahiAnnouncer *a;
+
+ assert(s);
+ assert(i);
+ assert(e);
+ assert(!e->dead);
+
+ if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_is_commited(e))
+ return;
+
+ /* We don't want duplicate announcers */
+ if (get_announcer(s, e, i))
+ return;
+
+ if ((!(a = avahi_new(AvahiAnnouncer, 1)))) {
+ avahi_log_error(__FILE__": Out of memory.");
+ return;
+ }
+
+ a->server = s;
+ a->interface = i;
+ a->entry = e;
+ a->time_event = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_interface, i->announcers, a);
+ AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_entry, e->announcers, a);
+
+ go_to_initial_state(a);
+}
+
+void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) {
+ AvahiEntry *e;
+
+ assert(s);
+ assert(i);
+
+ if (!i->announcing)
+ return;
+
+ for (e = s->entries; e; e = e->entries_next)
+ if (!e->dead)
+ new_announcer(s, i, e);
+}
+
+static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
+ AvahiEntry *e = userdata;
+
+ assert(m);
+ assert(i);
+ assert(e);
+ assert(!e->dead);
+
+ new_announcer(m->server, i, e);
+}
+
+void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) {
+ assert(s);
+ assert(e);
+ assert(!e->dead);
+
+ avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e);
+}
+
+void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g) {
+ AvahiEntry *e;
+
+ assert(s);
+ assert(g);
+
+ for (e = g->entries; e; e = e->by_group_next)
+ if (!e->dead)
+ avahi_announce_entry(s, e);
+}
+
+int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
+ AvahiAnnouncer *a;
+
+ assert(s);
+ assert(e);
+ assert(i);
+ assert(!e->dead);
+
+ if (!(a = get_announcer(s, e, i)))
+ return 0;
+
+ return
+ a->state == AVAHI_ANNOUNCING ||
+ a->state == AVAHI_ESTABLISHED ||
+ (a->state == AVAHI_WAITING && !(e->flags & AVAHI_PUBLISH_UNIQUE));
+}
+
+int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
+ AvahiAnnouncer *a;
+
+ assert(s);
+ assert(e);
+ assert(i);
+ assert(!e->dead);
+
+ if (!(a = get_announcer(s, e, i)))
+ return 0;
+
+ return
+ a->state == AVAHI_PROBING ||
+ (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE));
+}
+
+void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
+ AvahiAnnouncer *a;
+
+ assert(s);
+ assert(e);
+ assert(i);
+
+ if (!(a = get_announcer(s, e, i)))
+ return;
+
+ if (a->state == AVAHI_PROBING && a->entry->group)
+ a->entry->group->n_probing--;
+
+ go_to_initial_state(a);
+}
+
+static AvahiRecord *make_goodbye_record(AvahiRecord *r) {
+ AvahiRecord *g;
+
+ assert(r);
+
+ if (!(g = avahi_record_copy(r)))
+ return NULL; /* OOM */
+
+ assert(g->ref == 1);
+ g->ttl = 0;
+
+ return g;
+}
+
+static int is_duplicate_entry(AvahiServer *s, AvahiEntry *e) {
+ AvahiEntry *i;
+
+ assert(s);
+ assert(e);
+
+ for (i = avahi_hashmap_lookup(s->entries_by_key, e->record->key); i; i = i->by_key_next) {
+
+ if (i == e)
+ continue;
+
+ if (!avahi_record_equal_no_ttl(i->record, e->record))
+ continue;
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
+ AvahiEntry *e = userdata;
+ AvahiRecord *g;
+
+ assert(m);
+ assert(i);
+ assert(e);
+ assert(!e->dead);
+
+ if (!avahi_interface_match(i, e->interface, e->protocol))
+ return;
+
+ if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)
+ return;
+
+ if (!avahi_entry_is_registered(m->server, e, i))
+ return;
+
+ if (is_duplicate_entry(m->server, e))
+ return;
+
+ if (!(g = make_goodbye_record(e->record)))
+ return; /* OOM */
+
+ avahi_interface_post_response(i, g, e->flags & AVAHI_PUBLISH_UNIQUE, NULL, 1);
+ avahi_record_unref(g);
+}
+
+static void reannounce(AvahiAnnouncer *a) {
+ AvahiEntry *e;
+ struct timeval tv;
+
+ assert(a);
+ e = a->entry;
+
+ /* If the group this entry belongs to is not even commited, there's nothing to reannounce */
+ if (e->group && (e->group->state == AVAHI_ENTRY_GROUP_UNCOMMITED || e->group->state == AVAHI_ENTRY_GROUP_COLLISION))
+ return;
+
+ /* Because we might change state we decrease the probing counter first */
+ if (a->state == AVAHI_PROBING && a->entry->group)
+ a->entry->group->n_probing--;
+
+ if (a->state == AVAHI_PROBING ||
+ (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE)))
+
+ /* We were probing or waiting after probe, so we restart probing from the beginning here */
+
+ a->state = AVAHI_PROBING;
+ else if (a->state == AVAHI_WAITING)
+
+ /* We were waiting, but were not probing before, so we continue waiting */
+ a->state = AVAHI_WAITING;
+
+ else if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)
+
+ /* No announcer needed */
+ a->state = AVAHI_ESTABLISHED;
+
+ else {
+
+ /* Ok, let's restart announcing */
+ a->state = AVAHI_ANNOUNCING;
+ }
+
+ /* Now let's increase the probing counter again */
+ if (a->state == AVAHI_PROBING && e->group)
+ e->group->n_probing++;
+
+ a->n_iteration = 1;
+ a->sec_delay = 1;
+
+ if (a->state == AVAHI_PROBING)
+ set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC));
+ else if (a->state == AVAHI_ANNOUNCING)
+ set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC));
+ else
+ set_timeout(a, NULL);
+}
+
+
+static void reannounce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
+ AvahiEntry *e = userdata;
+ AvahiAnnouncer *a;
+
+ assert(m);
+ assert(i);
+ assert(e);
+ assert(!e->dead);
+
+ if (!(a = get_announcer(m->server, e, i)))
+ return;
+
+ reannounce(a);
+}
+
+void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e) {
+
+ assert(s);
+ assert(e);
+ assert(!e->dead);
+
+ avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, reannounce_walk_callback, e);
+}
+
+void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int remove) {
+ assert(s);
+ assert(i);
+
+ if (send_goodbye)
+ if (avahi_interface_is_relevant(i)) {
+ AvahiEntry *e;
+
+ for (e = s->entries; e; e = e->entries_next)
+ if (!e->dead)
+ send_goodbye_callback(s->monitor, i, e);
+ }
+
+ if (remove)
+ while (i->announcers)
+ remove_announcer(s, i->announcers);
+}
+
+void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int remove) {
+ assert(s);
+ assert(e);
+
+ if (send_goodbye)
+ if (!e->dead)
+ avahi_interface_monitor_walk(s->monitor, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, send_goodbye_callback, e);
+
+ if (remove)
+ while (e->announcers)
+ remove_announcer(s, e->announcers);
+}
+
diff --git a/trunk/avahi-core/announce.h b/trunk/avahi-core/announce.h
new file mode 100644
index 0000000..3a8bcf3
--- /dev/null
+++ b/trunk/avahi-core/announce.h
@@ -0,0 +1,71 @@
+#ifndef fooannouncehfoo
+#define fooannouncehfoo
+
+/* $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 AvahiAnnouncer AvahiAnnouncer;
+
+#include <avahi-common/llist.h>
+#include "iface.h"
+#include "internal.h"
+#include "timeeventq.h"
+#include "publish.h"
+
+typedef enum {
+ AVAHI_PROBING, /* probing phase */
+ AVAHI_WAITING, /* wait for other records in group */
+ AVAHI_ANNOUNCING, /* announcing phase */
+ AVAHI_ESTABLISHED /* we'e established */
+} AvahiAnnouncerState;
+
+struct AvahiAnnouncer {
+ AvahiServer *server;
+ AvahiInterface *interface;
+ AvahiEntry *entry;
+
+ AvahiTimeEvent *time_event;
+
+ AvahiAnnouncerState state;
+ unsigned n_iteration;
+ unsigned sec_delay;
+
+ AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_interface);
+ AVAHI_LLIST_FIELDS(AvahiAnnouncer, by_entry);
+};
+
+void avahi_announce_interface(AvahiServer *s, AvahiInterface *i);
+void avahi_announce_entry(AvahiServer *s, AvahiEntry *e);
+void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g);
+
+void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i);
+
+void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately);
+
+int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i);
+int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i);
+
+void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int rem);
+void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int rem);
+
+void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e);
+
+#endif
diff --git a/trunk/avahi-core/avahi-reflector.c b/trunk/avahi-core/avahi-reflector.c
new file mode 100644
index 0000000..df5539f
--- /dev/null
+++ b/trunk/avahi-core/avahi-reflector.c
@@ -0,0 +1,63 @@
+/* $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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <avahi-common/simple-watch.h>
+#include <avahi-core/core.h>
+
+int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char*argv[]) {
+ AvahiServer *server;
+ AvahiServerConfig config;
+ int error;
+ AvahiSimplePoll *simple_poll;
+
+ simple_poll = avahi_simple_poll_new();
+
+ avahi_server_config_init(&config);
+ config.publish_hinfo = 0;
+ config.publish_addresses = 0;
+ config.publish_workstation = 0;
+ config.publish_domain = 0;
+ config.use_ipv6 = 0;
+ config.enable_reflector = 1;
+
+ server = avahi_server_new(avahi_simple_poll_get(simple_poll), &config, NULL, NULL, &error);
+ avahi_server_config_free(&config);
+
+ 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/trunk/avahi-core/avahi-test.c b/trunk/avahi-core/avahi-test.c
new file mode 100644
index 0000000..cb7b97e
--- /dev/null
+++ b/trunk/avahi-core/avahi-test.c
@@ -0,0 +1,403 @@
+/* $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 <stdio.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <avahi-common/malloc.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/timeval.h>
+
+#include <avahi-core/core.h>
+#include <avahi-core/log.h>
+#include <avahi-core/publish.h>
+#include <avahi-core/lookup.h>
+#include <avahi-core/dns-srv-rr.h>
+
+static AvahiSEntryGroup *group = NULL;
+static AvahiServer *server = NULL;
+static char *service_name = NULL;
+
+static const AvahiPoll *poll_api;
+
+static void quit_timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void* userdata) {
+ AvahiSimplePoll *simple_poll = userdata;
+
+ avahi_simple_poll_quit(simple_poll);
+}
+
+static void dump_line(const char *text, AVAHI_GCC_UNUSED void* userdata) {
+ printf("%s\n", text);
+}
+
+static void dump_timeout_callback(AvahiTimeout *timeout, void* userdata) {
+ struct timeval tv;
+
+ AvahiServer *avahi = userdata;
+ avahi_server_dump(avahi, dump_line, NULL);
+
+ avahi_elapse_time(&tv, 5000, 0);
+ poll_api->timeout_update(timeout, &tv);
+}
+
+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";
+ }
+
+ abort();
+}
+
+static const char *resolver_event_to_string(AvahiResolverEvent event) {
+ switch (event) {
+ case AVAHI_RESOLVER_FOUND: return "FOUND";
+ case AVAHI_RESOLVER_FAILURE: return "FAILURE";
+ }
+ abort();
+}
+
+static void record_browser_callback(
+ AvahiSRecordBrowser *r,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED void* userdata) {
+ char *t;
+
+ assert(r);
+
+ 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));
+
+}
+
+static void remove_entries(void);
+static void create_entries(int new_name);
+
+static void entry_group_callback(AVAHI_GCC_UNUSED AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void* userdata) {
+ avahi_log_debug("entry group state: %i", state);
+
+ if (state == AVAHI_ENTRY_GROUP_COLLISION) {
+ remove_entries();
+ create_entries(1);
+ avahi_log_debug("Service name conflict, retrying with <%s>", service_name);
+ } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
+ avahi_log_debug("Service established under name <%s>", service_name);
+ }
+}
+
+static void server_callback(AvahiServer *s, AvahiServerState state, AVAHI_GCC_UNUSED void* userdata) {
+
+ server = s;
+ avahi_log_debug("server state: %i", state);
+
+ if (state == AVAHI_SERVER_RUNNING) {
+ 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;
+ remove_entries();
+
+ n = avahi_alternative_host_name(avahi_server_get_host_name(s));
+
+ avahi_log_debug("Host name conflict, retrying with <%s>", n);
+ avahi_server_set_host_name(s, n);
+ avahi_free(n);
+ }
+}
+
+static void remove_entries(void) {
+ if (group)
+ avahi_s_entry_group_reset(group);
+}
+
+static void create_entries(int new_name) {
+ AvahiAddress a;
+ AvahiRecord *r;
+
+ remove_entries();
+
+ if (!group)
+ group = avahi_s_entry_group_new(server, entry_group_callback, NULL);
+
+ assert(avahi_s_entry_group_is_empty(group));
+
+ if (!service_name)
+ service_name = avahi_strdup("Test Service");
+ else if (new_name) {
+ char *n = avahi_alternative_service_name(service_name);
+ avahi_free(service_name);
+ service_name = n;
+ }
+
+ if (avahi_server_add_service(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, service_name, "_http._tcp", NULL, NULL, 80, "foo", NULL) < 0) {
+ avahi_log_error("Failed to add HTTP service");
+ goto fail;
+ }
+
+ if (avahi_server_add_service(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, service_name, "_ftp._tcp", NULL, NULL, 21, "foo", NULL) < 0) {
+ avahi_log_error("Failed to add FTP service");
+ goto fail;
+ }
+
+ if (avahi_server_add_service(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0,service_name, "_webdav._tcp", NULL, NULL, 80, "foo", NULL) < 0) {
+ avahi_log_error("Failed to add WEBDAV service");
+ goto fail;
+ }
+
+ if (avahi_server_add_dns_server_address(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, NULL, AVAHI_DNS_SERVER_RESOLVE, avahi_address_parse("192.168.50.1", AVAHI_PROTO_UNSPEC, &a), 53) < 0) {
+ avahi_log_error("Failed to add new DNS Server address");
+ goto fail;
+ }
+
+ r = avahi_record_new_full("cname.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME, AVAHI_DEFAULT_TTL);
+ r->data.cname.name = avahi_strdup("cocaine.local");
+
+ if (avahi_server_add(server, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, r) < 0) {
+ avahi_record_unref(r);
+ avahi_log_error("Failed to add CNAME record");
+ goto fail;
+ }
+ avahi_record_unref(r);
+
+ avahi_s_entry_group_commit(group);
+ return;
+
+fail:
+ if (group)
+ avahi_s_entry_group_free(group);
+
+ group = NULL;
+}
+
+static void hnr_callback(
+ AVAHI_GCC_UNUSED AvahiSHostNameResolver *r,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *hostname,
+ const AvahiAddress *a,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED void* userdata) {
+ char t[AVAHI_ADDRESS_STR_MAX];
+
+ 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", resolver_event_to_string(event));
+}
+
+static void ar_callback(
+ AVAHI_GCC_UNUSED AvahiSAddressResolver *r,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const AvahiAddress *a,
+ const char *hostname,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED void* userdata) {
+ char t[AVAHI_ADDRESS_STR_MAX];
+
+ avahi_address_snprint(t, sizeof(t), a);
+
+ 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(
+ AVAHI_GCC_UNUSED AvahiSDomainBrowser *b,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED void* userdata) {
+
+ avahi_log_debug("DB: (%i.%i) <%s> [%s]", iface, protocol, domain ? domain : "NULL", browser_event_to_string(event));
+}
+
+static void stb_callback(
+ AVAHI_GCC_UNUSED AvahiSServiceTypeBrowser *b,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *service_type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED void* userdata) {
+
+ avahi_log_debug("STB: (%i.%i) %s in <%s> [%s]", iface, protocol, service_type ? service_type : "NULL", domain ? domain : "NULL", browser_event_to_string(event));
+}
+
+static void sb_callback(
+ AVAHI_GCC_UNUSED AvahiSServiceBrowser *b,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char *name,
+ const char *service_type,
+ const char *domain,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED void* userdata) {
+ avahi_log_debug("SB: (%i.%i) <%s> as %s in <%s> [%s]", iface, protocol, name ? name : "NULL", service_type ? service_type : "NULL", domain ? domain : "NULL", browser_event_to_string(event));
+}
+
+static void sr_callback(
+ AVAHI_GCC_UNUSED 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,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED 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[AVAHI_ADDRESS_STR_MAX], *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) [%s]", iface, protocol, name, service_type, domain_name, hostname, t, port, s, resolver_event_to_string(event));
+ avahi_free(s);
+ }
+}
+
+static void dsb_callback(
+ AVAHI_GCC_UNUSED AvahiSDNSServerBrowser *b,
+ AvahiIfIndex iface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ const char*hostname,
+ const AvahiAddress *a,
+ uint16_t port,
+ AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+ AVAHI_GCC_UNUSED void* userdata) {
+
+ char t[AVAHI_ADDRESS_STR_MAX] = "n/a";
+
+ if (a)
+ avahi_address_snprint(t, sizeof(t), a);
+
+ avahi_log_debug("DSB: (%i.%i): %s/%s:%i [%s]", iface, protocol, hostname ? hostname : "NULL", t, port, browser_event_to_string(event));
+}
+
+int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) {
+ AvahiSRecordBrowser *r;
+ AvahiSHostNameResolver *hnr;
+ AvahiSAddressResolver *ar;
+ AvahiKey *k;
+ AvahiServerConfig config;
+ AvahiAddress a;
+ AvahiSDomainBrowser *db;
+ AvahiSServiceTypeBrowser *stb;
+ AvahiSServiceBrowser *sb;
+ AvahiSServiceResolver *sr;
+ AvahiSDNSServerBrowser *dsb;
+ AvahiSimplePoll *simple_poll;
+ int error;
+ struct timeval tv;
+
+ simple_poll = avahi_simple_poll_new();
+ poll_api = avahi_simple_poll_get(simple_poll);
+
+ avahi_server_config_init(&config);
+
+ 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.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, "cname.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.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, 0, db_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, 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, 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, 0, dsb_callback, NULL);
+
+ avahi_elapse_time(&tv, 1000*5, 0);
+ poll_api->timeout_new(poll_api, &tv, dump_timeout_callback, server);
+
+ avahi_elapse_time(&tv, 1000*60, 0);
+ poll_api->timeout_new(poll_api, &tv, quit_timeout_callback, simple_poll);
+
+ avahi_simple_poll_loop(simple_poll);
+
+ avahi_s_record_browser_free(r);
+ avahi_s_host_name_resolver_free(hnr);
+ avahi_s_address_resolver_free(ar);
+ avahi_s_service_type_browser_free(stb);
+ avahi_s_service_browser_free(sb);
+ avahi_s_service_resolver_free(sr);
+ avahi_s_dns_server_browser_free(dsb);
+
+ if (group)
+ avahi_s_entry_group_free(group);
+
+ if (server)
+ avahi_server_free(server);
+
+ if (simple_poll)
+ avahi_simple_poll_free(simple_poll);
+
+ avahi_free(service_name);
+
+ return 0;
+}
diff --git a/trunk/avahi-core/browse-dns-server.c b/trunk/avahi-core/browse-dns-server.c
new file mode 100644
index 0000000..48d23ad
--- /dev/null
+++ b/trunk/avahi-core/browse-dns-server.c
@@ -0,0 +1,324 @@
+/* $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 <string.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+#include "rr.h"
+
+typedef struct AvahiDNSServerInfo AvahiDNSServerInfo;
+
+struct AvahiDNSServerInfo {
+ AvahiSDNSServerBrowser *browser;
+
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ AvahiRecord *srv_record;
+ AvahiSHostNameResolver *host_name_resolver;
+ AvahiAddress address;
+ AvahiLookupResultFlags flags;
+
+ AVAHI_LLIST_FIELDS(AvahiDNSServerInfo, info);
+};
+
+struct AvahiSDNSServerBrowser {
+ AvahiServer *server;
+
+ AvahiSRecordBrowser *record_browser;
+ AvahiSDNSServerBrowserCallback callback;
+ void* userdata;
+ AvahiProtocol aprotocol;
+ AvahiLookupFlags user_flags;
+
+ unsigned n_info;
+
+ AVAHI_LLIST_FIELDS(AvahiSDNSServerBrowser, browser);
+ AVAHI_LLIST_HEAD(AvahiDNSServerInfo, info);
+};
+
+static AvahiDNSServerInfo* get_server_info(AvahiSDNSServerBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r) {
+ AvahiDNSServerInfo *i;
+
+ assert(b);
+ assert(r);
+
+ for (i = b->info; i; i = i->info_next)
+ if (i->interface == interface &&
+ i->protocol == protocol &&
+ avahi_record_equal_no_ttl(r, i->srv_record))
+ return i;
+
+ return NULL;
+}
+
+static void server_info_free(AvahiSDNSServerBrowser *b, AvahiDNSServerInfo *i) {
+ assert(b);
+ assert(i);
+
+ avahi_record_unref(i->srv_record);
+ if (i->host_name_resolver)
+ avahi_s_host_name_resolver_free(i->host_name_resolver);
+
+ AVAHI_LLIST_REMOVE(AvahiDNSServerInfo, info, b->info, i);
+
+ assert(b->n_info >= 1);
+ b->n_info--;
+
+ avahi_free(i);
+}
+
+static void host_name_resolver_callback(
+ AvahiSHostNameResolver *r,
+ AVAHI_GCC_UNUSED AvahiIfIndex interface,
+ AVAHI_GCC_UNUSED AvahiProtocol protocol,
+ AvahiResolverEvent event,
+ const char *host_name,
+ const AvahiAddress *a,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ AvahiDNSServerInfo *i = userdata;
+
+ assert(r);
+ assert(host_name);
+ assert(i);
+
+ switch (event) {
+ case AVAHI_RESOLVER_FOUND: {
+ i->address = *a;
+
+ i->browser->callback(
+ i->browser,
+ i->interface,
+ i->protocol,
+ AVAHI_BROWSER_NEW,
+ i->srv_record->data.srv.name,
+ &i->address,
+ i->srv_record->data.srv.port,
+ i->flags | flags,
+ i->browser->userdata);
+
+ break;
+ }
+
+ case AVAHI_RESOLVER_FAILURE:
+ /* Ignore */
+ break;
+ }
+
+ avahi_s_host_name_resolver_free(i->host_name_resolver);
+ i->host_name_resolver = NULL;
+}
+
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ AvahiSDNSServerBrowser *b = userdata;
+
+ assert(rr);
+ assert(b);
+
+ /* Filter flags */
+ flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
+
+ switch (event) {
+ case AVAHI_BROWSER_NEW: {
+ AvahiDNSServerInfo *i;
+
+ assert(record);
+ assert(record->key->type == AVAHI_DNS_TYPE_SRV);
+
+ if (get_server_info(b, interface, protocol, record))
+ return;
+
+ if (b->n_info >= 10)
+ return;
+
+ if (!(i = avahi_new(AvahiDNSServerInfo, 1)))
+ return; /* OOM */
+
+ i->browser = b;
+ i->interface = interface;
+ i->protocol = protocol;
+ i->srv_record = avahi_record_ref(record);
+ i->host_name_resolver = avahi_s_host_name_resolver_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_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,
+ AvahiLookupFlags flags,
+ AvahiSDNSServerBrowserCallback callback,
+ void* userdata) {
+
+ static const char * const type_table[AVAHI_DNS_SERVER_MAX] = {
+ "_domain._udp",
+ "_dns-update._udp"
+ };
+
+ AvahiSDNSServerBrowser *b;
+ AvahiKey *k = NULL;
+ char n[AVAHI_DOMAIN_NAME_MAX];
+ int r;
+
+ assert(server);
+ assert(callback);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DNS_SERVER_MAX, AVAHI_ERR_INVALID_FLAGS);
+
+ if (!domain)
+ domain = server->domain_name;
+
+ if ((r = avahi_service_name_join(n, sizeof(n), NULL, type_table[type], domain)) < 0) {
+ avahi_server_set_errno(server, r);
+ return NULL;
+ }
+
+ if (!(b = avahi_new(AvahiSDNSServerBrowser, 1))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ b->server = server;
+ b->callback = callback;
+ b->userdata = userdata;
+ b->aprotocol = aprotocol;
+ b->n_info = 0;
+ b->user_flags = flags;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiDNSServerInfo, b->info);
+ AVAHI_LLIST_PREPEND(AvahiSDNSServerBrowser, browser, server->dns_server_browsers, b);
+
+ if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b)))
+ goto fail;
+
+ avahi_key_unref(k);
+
+ return b;
+
+fail:
+
+ if (k)
+ avahi_key_unref(k);
+
+ avahi_s_dns_server_browser_free(b);
+ return NULL;
+}
+
+void avahi_s_dns_server_browser_free(AvahiSDNSServerBrowser *b) {
+ assert(b);
+
+ while (b->info)
+ server_info_free(b, b->info);
+
+ AVAHI_LLIST_REMOVE(AvahiSDNSServerBrowser, browser, b->server->dns_server_browsers, b);
+
+ if (b->record_browser)
+ avahi_s_record_browser_free(b->record_browser);
+
+ avahi_free(b);
+}
+
diff --git a/trunk/avahi-core/browse-domain.c b/trunk/avahi-core/browse-domain.c
new file mode 100644
index 0000000..0043806
--- /dev/null
+++ b/trunk/avahi-core/browse-domain.c
@@ -0,0 +1,237 @@
+/* $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 <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+struct AvahiSDomainBrowser {
+ int ref;
+
+ AvahiServer *server;
+
+ AvahiSRecordBrowser *record_browser;
+
+ AvahiDomainBrowserType type;
+ AvahiSDomainBrowserCallback callback;
+ void* userdata;
+
+ AvahiTimeEvent *defer_event;
+
+ int all_for_now_scheduled;
+
+ AVAHI_LLIST_FIELDS(AvahiSDomainBrowser, browser);
+};
+
+static void inc_ref(AvahiSDomainBrowser *b) {
+ assert(b);
+ assert(b->ref >= 1);
+
+ b->ref++;
+}
+
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ AvahiSDomainBrowser *b = userdata;
+ char *n = NULL;
+
+ assert(rr);
+ assert(b);
+
+ if (event == AVAHI_BROWSER_ALL_FOR_NOW &&
+ b->defer_event) {
+
+ b->all_for_now_scheduled = 1;
+ return;
+ }
+
+ /* Filter flags */
+ flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
+
+ if (record) {
+ assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+ n = record->data.ptr.name;
+
+ if (b->type == AVAHI_DOMAIN_BROWSER_BROWSE) {
+ AvahiStringList *l;
+
+ /* Filter out entries defined statically */
+
+ for (l = b->server->config.browse_domains; l; l = l->next)
+ if (avahi_domain_equal((char*) l->text, n))
+ return;
+ }
+
+ }
+
+ b->callback(b, interface, protocol, event, n, flags, b->userdata);
+}
+
+static void defer_callback(AvahiTimeEvent *e, void *userdata) {
+ AvahiSDomainBrowser *b = userdata;
+ AvahiStringList *l;
+
+ assert(e);
+ assert(b);
+
+ assert(b->type == AVAHI_DOMAIN_BROWSER_BROWSE);
+
+ avahi_time_event_free(b->defer_event);
+ b->defer_event = NULL;
+
+ /* Increase ref counter */
+ inc_ref(b);
+
+ for (l = b->server->config.browse_domains; l; l = l->next) {
+
+ /* Check whether this object still exists outside our own
+ * stack frame */
+ if (b->ref <= 1)
+ break;
+
+ b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, b->userdata);
+ }
+
+ if (b->ref > 1) {
+ /* If the ALL_FOR_NOW event has already been scheduled, execute it now */
+
+ if (b->all_for_now_scheduled)
+ b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_ALL_FOR_NOW, NULL, 0, b->userdata);
+ }
+
+ /* Decrease ref counter */
+ avahi_s_domain_browser_free(b);
+}
+
+AvahiSDomainBrowser *avahi_s_domain_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *domain,
+ AvahiDomainBrowserType type,
+ AvahiLookupFlags flags,
+ AvahiSDomainBrowserCallback callback,
+ void* userdata) {
+
+ static const char * const type_table[AVAHI_DOMAIN_BROWSER_MAX] = {
+ "b",
+ "db",
+ "r",
+ "dr",
+ "lb"
+ };
+
+ AvahiSDomainBrowser *b;
+ AvahiKey *k = NULL;
+ char n[AVAHI_DOMAIN_NAME_MAX];
+ int r;
+
+ assert(server);
+ assert(callback);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, type < AVAHI_DOMAIN_BROWSER_MAX, AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+ if (!domain)
+ domain = server->domain_name;
+
+ if ((r = avahi_service_name_join(n, sizeof(n), type_table[type], "_dns-sd._udp", domain)) < 0) {
+ avahi_server_set_errno(server, r);
+ return NULL;
+ }
+
+ if (!(b = avahi_new(AvahiSDomainBrowser, 1))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ b->ref = 1;
+ b->server = server;
+ b->callback = callback;
+ b->userdata = userdata;
+ b->record_browser = NULL;
+ b->type = type;
+ b->all_for_now_scheduled = 0;
+ b->defer_event = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiSDomainBrowser, browser, server->domain_browsers, b);
+
+ if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b)))
+ goto fail;
+
+ avahi_key_unref(k);
+
+ if (type == AVAHI_DOMAIN_BROWSER_BROWSE && b->server->config.browse_domains)
+ b->defer_event = avahi_time_event_new(server->time_event_queue, NULL, defer_callback, b);
+
+ return b;
+
+fail:
+
+ if (k)
+ avahi_key_unref(k);
+
+ avahi_s_domain_browser_free(b);
+
+ return NULL;
+}
+
+void avahi_s_domain_browser_free(AvahiSDomainBrowser *b) {
+ assert(b);
+
+ assert(b->ref >= 1);
+ if (--b->ref > 0)
+ return;
+
+ AVAHI_LLIST_REMOVE(AvahiSDomainBrowser, browser, b->server->domain_browsers, b);
+
+ if (b->record_browser)
+ avahi_s_record_browser_free(b->record_browser);
+
+ if (b->defer_event)
+ avahi_time_event_free(b->defer_event);
+
+ avahi_free(b);
+}
diff --git a/trunk/avahi-core/browse-service-type.c b/trunk/avahi-core/browse-service-type.c
new file mode 100644
index 0000000..252e3cb
--- /dev/null
+++ b/trunk/avahi-core/browse-service-type.c
@@ -0,0 +1,159 @@
+/* $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 <string.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+struct AvahiSServiceTypeBrowser {
+ AvahiServer *server;
+ char *domain_name;
+
+ AvahiSRecordBrowser *record_browser;
+
+ AvahiSServiceTypeBrowserCallback callback;
+ void* userdata;
+
+ AVAHI_LLIST_FIELDS(AvahiSServiceTypeBrowser, browser);
+};
+
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ AvahiSServiceTypeBrowser *b = userdata;
+
+ assert(rr);
+ assert(b);
+
+ /* Filter flags */
+ flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
+
+ if (record) {
+ char type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX];
+
+ assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+ if (avahi_service_name_split(record->data.ptr.name, NULL, 0, type, sizeof(type), domain, sizeof(domain)) < 0) {
+ avahi_log_warn("Invalid service type '%s'", record->key->name);
+ return;
+ }
+
+ b->callback(b, interface, protocol, event, type, domain, flags, b->userdata);
+ } else
+ b->callback(b, interface, protocol, event, NULL, b->domain_name, flags, b->userdata);
+}
+
+AvahiSServiceTypeBrowser *avahi_s_service_type_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *domain,
+ AvahiLookupFlags flags,
+ AvahiSServiceTypeBrowserCallback callback,
+ void* userdata) {
+
+ AvahiSServiceTypeBrowser *b;
+ AvahiKey *k = NULL;
+ char n[AVAHI_DOMAIN_NAME_MAX];
+ int r;
+
+ assert(server);
+ assert(callback);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+ if (!domain)
+ domain = server->domain_name;
+
+ if ((r = avahi_service_name_join(n, sizeof(n), NULL, "_services._dns-sd._udp", domain)) < 0) {
+ avahi_server_set_errno(server, r);
+ return NULL;
+ }
+
+ if (!(b = avahi_new(AvahiSServiceTypeBrowser, 1))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ b->server = server;
+ b->callback = callback;
+ b->userdata = userdata;
+ b->record_browser = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiSServiceTypeBrowser, browser, server->service_type_browsers, b);
+
+ if (!(b->domain_name = avahi_normalize_name_strdup(domain))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b)))
+ goto fail;
+
+ avahi_key_unref(k);
+
+ return b;
+
+fail:
+ if (k)
+ avahi_key_unref(k);
+
+ avahi_s_service_type_browser_free(b);
+
+ return NULL;
+}
+
+void avahi_s_service_type_browser_free(AvahiSServiceTypeBrowser *b) {
+ assert(b);
+
+ AVAHI_LLIST_REMOVE(AvahiSServiceTypeBrowser, browser, b->server->service_type_browsers, b);
+
+ if (b->record_browser)
+ avahi_s_record_browser_free(b->record_browser);
+
+ avahi_free(b->domain_name);
+ avahi_free(b);
+}
+
+
diff --git a/trunk/avahi-core/browse-service.c b/trunk/avahi-core/browse-service.c
new file mode 100644
index 0000000..43778dd
--- /dev/null
+++ b/trunk/avahi-core/browse-service.c
@@ -0,0 +1,169 @@
+/* $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 <string.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+struct AvahiSServiceBrowser {
+ AvahiServer *server;
+ char *domain_name;
+ char *service_type;
+
+ AvahiSRecordBrowser *record_browser;
+
+ AvahiSServiceBrowserCallback callback;
+ void* userdata;
+
+ AVAHI_LLIST_FIELDS(AvahiSServiceBrowser, browser);
+};
+
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ AvahiSServiceBrowser *b = userdata;
+
+ assert(rr);
+ assert(b);
+
+ /* Filter flags */
+ flags &= AVAHI_LOOKUP_RESULT_CACHED | AVAHI_LOOKUP_RESULT_MULTICAST | AVAHI_LOOKUP_RESULT_WIDE_AREA;
+
+ if (record) {
+ char service[AVAHI_LABEL_MAX], type[AVAHI_DOMAIN_NAME_MAX], domain[AVAHI_DOMAIN_NAME_MAX];
+
+ assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+ if (event == AVAHI_BROWSER_NEW && avahi_server_is_service_local(b->server, interface, protocol, record->data.ptr.name))
+ flags |= AVAHI_LOOKUP_RESULT_LOCAL;
+
+ if (avahi_service_name_split(record->data.ptr.name, service, sizeof(service), type, sizeof(type), domain, sizeof(domain)) < 0) {
+ avahi_log_warn("Failed to split '%s'", record->key->name);
+ return;
+ }
+
+ b->callback(b, interface, protocol, event, service, type, domain, flags, b->userdata);
+
+ } else
+ b->callback(b, interface, protocol, event, NULL, b->service_type, b->domain_name, flags, b->userdata);
+
+}
+
+AvahiSServiceBrowser *avahi_s_service_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ const char *service_type,
+ const char *domain,
+ AvahiLookupFlags flags,
+ AvahiSServiceBrowserCallback callback,
+ void* userdata) {
+
+ AvahiSServiceBrowser *b;
+ AvahiKey *k = NULL;
+ char n[AVAHI_DOMAIN_NAME_MAX];
+ int r;
+
+ assert(server);
+ assert(callback);
+ assert(service_type);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_generic(service_type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+
+ if (!domain)
+ domain = server->domain_name;
+
+ if ((r = avahi_service_name_join(n, sizeof(n), NULL, service_type, domain)) < 0) {
+ avahi_server_set_errno(server, r);
+ return NULL;
+ }
+
+ if (!(b = avahi_new(AvahiSServiceBrowser, 1))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ b->server = server;
+ b->domain_name = b->service_type = NULL;
+ b->callback = callback;
+ b->userdata = userdata;
+ b->record_browser = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiSServiceBrowser, browser, server->service_browsers, b);
+
+ if (!(b->domain_name = avahi_normalize_name_strdup(domain)) ||
+ !(b->service_type = avahi_normalize_name_strdup(service_type))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ if (!(b->record_browser = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, b)))
+ goto fail;
+
+ avahi_key_unref(k);
+
+ return b;
+
+fail:
+
+ if (k)
+ avahi_key_unref(k);
+
+ avahi_s_service_browser_free(b);
+ return NULL;
+}
+
+void avahi_s_service_browser_free(AvahiSServiceBrowser *b) {
+ assert(b);
+
+ AVAHI_LLIST_REMOVE(AvahiSServiceBrowser, browser, b->server->service_browsers, b);
+
+ if (b->record_browser)
+ avahi_s_record_browser_free(b->record_browser);
+
+ avahi_free(b->domain_name);
+ avahi_free(b->service_type);
+ avahi_free(b);
+}
diff --git a/trunk/avahi-core/browse.c b/trunk/avahi-core/browse.c
new file mode 100644
index 0000000..16d8954
--- /dev/null
+++ b/trunk/avahi-core/browse.c
@@ -0,0 +1,615 @@
+/* $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 <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/rlist.h>
+#include <avahi-common/address.h>
+
+#include "browse.h"
+#include "log.h"
+#include "querier.h"
+#include "domain-util.h"
+#include "rr-util.h"
+
+#define AVAHI_LOOKUPS_PER_BROWSER_MAX 15
+
+struct AvahiSRBLookup {
+ AvahiSRecordBrowser *record_browser;
+
+ unsigned ref;
+
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ AvahiLookupFlags flags;
+
+ AvahiKey *key;
+
+ AvahiWideAreaLookup *wide_area;
+ AvahiMulticastLookup *multicast;
+
+ AvahiRList *cname_lookups;
+
+ AVAHI_LLIST_FIELDS(AvahiSRBLookup, lookups);
+};
+
+static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r);
+static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r);
+
+static void transport_flags_from_domain(AvahiServer *s, AvahiLookupFlags *flags, const char *domain) {
+ assert(flags);
+ assert(domain);
+
+ assert(!((*flags & AVAHI_LOOKUP_USE_MULTICAST) && (*flags & AVAHI_LOOKUP_USE_WIDE_AREA)));
+
+ if (*flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA))
+ return;
+
+ if (!s->wide_area_lookup_engine ||
+ !avahi_wide_area_has_servers(s->wide_area_lookup_engine) ||
+ avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_LOCAL) ||
+ avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV4) ||
+ avahi_domain_ends_with(domain, AVAHI_MDNS_SUFFIX_ADDR_IPV6))
+ *flags |= AVAHI_LOOKUP_USE_MULTICAST;
+ else
+ *flags |= AVAHI_LOOKUP_USE_WIDE_AREA;
+}
+
+static AvahiSRBLookup* lookup_new(
+ AvahiSRecordBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiLookupFlags flags,
+ AvahiKey *key) {
+
+ AvahiSRBLookup *l;
+
+ assert(b);
+ assert(AVAHI_IF_VALID(interface));
+ assert(AVAHI_PROTO_VALID(protocol));
+
+ if (b->n_lookups >= AVAHI_LOOKUPS_PER_BROWSER_MAX)
+ /* We don't like cyclic CNAMEs */
+ return NULL;
+
+ if (!(l = avahi_new(AvahiSRBLookup, 1)))
+ return NULL;
+
+ l->ref = 1;
+ l->record_browser = b;
+ l->interface = interface;
+ l->protocol = protocol;
+ l->key = avahi_key_ref(key);
+ l->wide_area = NULL;
+ l->multicast = NULL;
+ l->cname_lookups = NULL;
+ l->flags = flags;
+
+ transport_flags_from_domain(b->server, &l->flags, key->name);
+
+ AVAHI_LLIST_PREPEND(AvahiSRBLookup, lookups, b->lookups, l);
+
+ b->n_lookups ++;
+
+ return l;
+}
+
+static void lookup_unref(AvahiSRBLookup *l) {
+ assert(l);
+ assert(l->ref >= 1);
+
+ if (--l->ref >= 1)
+ return;
+
+ AVAHI_LLIST_REMOVE(AvahiSRBLookup, lookups, l->record_browser->lookups, l);
+ l->record_browser->n_lookups --;
+
+ if (l->wide_area) {
+ avahi_wide_area_lookup_free(l->wide_area);
+ l->wide_area = NULL;
+ }
+
+ if (l->multicast) {
+ avahi_multicast_lookup_free(l->multicast);
+ l->multicast = NULL;
+ }
+
+ while (l->cname_lookups) {
+ lookup_unref(l->cname_lookups->data);
+ l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, l->cname_lookups);
+ }
+
+ avahi_key_unref(l->key);
+ avahi_free(l);
+}
+
+static AvahiSRBLookup* lookup_ref(AvahiSRBLookup *l) {
+ assert(l);
+ assert(l->ref >= 1);
+
+ l->ref++;
+ return l;
+}
+
+static AvahiSRBLookup *lookup_find(
+ AvahiSRecordBrowser *b,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiLookupFlags flags,
+ AvahiKey *key) {
+
+ AvahiSRBLookup *l;
+
+ assert(b);
+
+ for (l = b->lookups; l; l = l->lookups_next) {
+
+ if ((l->interface == AVAHI_IF_UNSPEC || l->interface == interface) &&
+ (l->interface == AVAHI_PROTO_UNSPEC || l->protocol == protocol) &&
+ l->flags == flags &&
+ avahi_key_equal(l->key, key))
+
+ return l;
+ }
+
+ return NULL;
+}
+
+static void browser_cancel(AvahiSRecordBrowser *b) {
+ assert(b);
+
+ if (b->root_lookup) {
+ lookup_unref(b->root_lookup);
+ b->root_lookup = NULL;
+ }
+
+ if (b->defer_time_event) {
+ avahi_time_event_free(b->defer_time_event);
+ b->defer_time_event = NULL;
+ }
+}
+
+static void lookup_wide_area_callback(
+ AvahiWideAreaLookupEngine *e,
+ AvahiBrowserEvent event,
+ AvahiLookupResultFlags flags,
+ AvahiRecord *r,
+ void *userdata) {
+
+ AvahiSRBLookup *l = userdata;
+ AvahiSRecordBrowser *b;
+
+ assert(e);
+ assert(l);
+ assert(l->ref >= 1);
+
+ b = l->record_browser;
+
+ if (b->dead)
+ return;
+
+ lookup_ref(l);
+
+ switch (event) {
+ case AVAHI_BROWSER_NEW:
+ assert(r);
+
+ if (r->key->clazz == AVAHI_DNS_CLASS_IN &&
+ r->key->type == AVAHI_DNS_TYPE_CNAME)
+ /* It's a CNAME record, so let's follow it. We only follow it on wide area DNS! */
+ lookup_handle_cname(l, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_LOOKUP_USE_WIDE_AREA, r);
+ else {
+ /* It's a normal record, so let's call the user callback */
+ assert(avahi_key_equal(r->key, l->key));
+
+ b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, r, flags, b->userdata);
+ }
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ /* Not defined for wide area DNS */
+ abort();
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ case AVAHI_BROWSER_FAILURE:
+
+ b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata);
+ break;
+ }
+
+ lookup_unref(l);
+
+}
+
+static void lookup_multicast_callback(
+ AvahiMulticastLookupEngine *e,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiLookupResultFlags flags,
+ AvahiRecord *r,
+ void *userdata) {
+
+ AvahiSRBLookup *l = userdata;
+ AvahiSRecordBrowser *b;
+
+ assert(e);
+ assert(l);
+
+ b = l->record_browser;
+
+ if (b->dead)
+ return;
+
+ lookup_ref(l);
+
+ switch (event) {
+ case AVAHI_BROWSER_NEW:
+ assert(r);
+
+ if (r->key->clazz == AVAHI_DNS_CLASS_IN &&
+ r->key->type == AVAHI_DNS_TYPE_CNAME)
+ /* It's a CNAME record, so let's follow it. We allow browsing on both multicast and wide area. */
+ lookup_handle_cname(l, interface, protocol, b->flags, r);
+ else {
+ /* It's a normal record, so let's call the user callback */
+
+ if (avahi_server_is_record_local(b->server, interface, protocol, r))
+ flags |= AVAHI_LOOKUP_RESULT_LOCAL;
+
+ b->callback(b, interface, protocol, event, r, flags, b->userdata);
+ }
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ assert(r);
+
+ if (r->key->clazz == AVAHI_DNS_CLASS_IN &&
+ r->key->type == AVAHI_DNS_TYPE_CNAME)
+ /* It's a CNAME record, so let's drop that query! */
+ lookup_drop_cname(l, interface, protocol, 0, r);
+ else {
+ /* It's a normal record, so let's call the user callback */
+ assert(avahi_key_equal(b->key, l->key));
+
+ b->callback(b, interface, protocol, event, r, flags, b->userdata);
+ }
+ break;
+
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+
+ b->callback(b, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, event, NULL, flags, b->userdata);
+ break;
+
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ case AVAHI_BROWSER_FAILURE:
+ /* Not defined for multicast DNS */
+ abort();
+
+ }
+
+ lookup_unref(l);
+}
+
+static int lookup_start(AvahiSRBLookup *l) {
+ assert(l);
+
+ assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST));
+ assert(!l->wide_area && !l->multicast);
+
+ if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) {
+
+ if (!(l->wide_area = avahi_wide_area_lookup_new(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l)))
+ return -1;
+
+ } else {
+ assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST);
+
+ if (!(l->multicast = avahi_multicast_lookup_new(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l)))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int lookup_scan_cache(AvahiSRBLookup *l) {
+ int n = 0;
+
+ assert(l);
+
+ assert(!(l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) != !(l->flags & AVAHI_LOOKUP_USE_MULTICAST));
+
+
+ if (l->flags & AVAHI_LOOKUP_USE_WIDE_AREA) {
+ n = (int) avahi_wide_area_scan_cache(l->record_browser->server->wide_area_lookup_engine, l->key, lookup_wide_area_callback, l);
+
+ } else {
+ assert(l->flags & AVAHI_LOOKUP_USE_MULTICAST);
+ n = (int) avahi_multicast_lookup_engine_scan_cache(l->record_browser->server->multicast_lookup_engine, l->interface, l->protocol, l->key, lookup_multicast_callback, l);
+ }
+
+ return n;
+}
+
+static AvahiSRBLookup* lookup_add(AvahiSRecordBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiKey *key) {
+ AvahiSRBLookup *l;
+
+ assert(b);
+ assert(!b->dead);
+
+ if ((l = lookup_find(b, interface, protocol, flags, key)))
+ return lookup_ref(l);
+
+ if (!(l = lookup_new(b, interface, protocol, flags, key)))
+ return NULL;
+
+ return l;
+}
+
+static int lookup_go(AvahiSRBLookup *l) {
+ int n = 0;
+ assert(l);
+
+ if (l->record_browser->dead)
+ return 0;
+
+ lookup_ref(l);
+
+ /* Browse the cache for the root request */
+ n = lookup_scan_cache(l);
+
+ /* Start the lookup */
+ if (!l->record_browser->dead && l->ref > 1) {
+
+ if ((l->flags & AVAHI_LOOKUP_USE_MULTICAST) || n == 0)
+ /* We do no start a query if the cache contained entries and we're on wide area */
+
+ if (lookup_start(l) < 0)
+ n = -1;
+ }
+
+ lookup_unref(l);
+
+ return n;
+}
+
+static void lookup_handle_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) {
+ AvahiKey *k;
+ AvahiSRBLookup *n;
+
+ assert(l);
+ assert(r);
+
+ assert(r->key->clazz == AVAHI_DNS_CLASS_IN);
+ assert(r->key->type == AVAHI_DNS_TYPE_CNAME);
+
+ k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type);
+ n = lookup_add(l->record_browser, interface, protocol, flags, k);
+ avahi_key_unref(k);
+
+ if (!n) {
+ avahi_log_debug(__FILE__": Failed to create SRBLookup.");
+ return;
+ }
+
+ l->cname_lookups = avahi_rlist_prepend(l->cname_lookups, lookup_ref(n));
+
+ lookup_go(n);
+ lookup_unref(n);
+}
+
+static void lookup_drop_cname(AvahiSRBLookup *l, AvahiIfIndex interface, AvahiProtocol protocol, AvahiLookupFlags flags, AvahiRecord *r) {
+ AvahiKey *k;
+ AvahiSRBLookup *n = NULL;
+ AvahiRList *rl;
+
+ assert(r->key->clazz == AVAHI_DNS_CLASS_IN);
+ assert(r->key->type == AVAHI_DNS_TYPE_CNAME);
+
+ k = avahi_key_new(r->data.ptr.name, l->record_browser->key->clazz, l->record_browser->key->type);
+
+ for (rl = l->cname_lookups; rl; rl = rl->rlist_next) {
+ n = rl->data;
+
+ assert(n);
+
+ if ((n->interface == AVAHI_IF_UNSPEC || n->interface == interface) &&
+ (n->interface == AVAHI_PROTO_UNSPEC || n->protocol == protocol) &&
+ n->flags == flags &&
+ avahi_key_equal(n->key, k))
+ break;
+ }
+
+ avahi_key_unref(k);
+
+ if (rl) {
+ l->cname_lookups = avahi_rlist_remove_by_link(l->cname_lookups, rl);
+ lookup_unref(n);
+ }
+}
+
+static void defer_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) {
+ AvahiSRecordBrowser *b = userdata;
+ int n;
+
+ assert(b);
+ assert(!b->dead);
+
+ /* Remove the defer timeout */
+ if (b->defer_time_event) {
+ avahi_time_event_free(b->defer_time_event);
+ b->defer_time_event = NULL;
+ }
+
+ /* Create initial query */
+ assert(!b->root_lookup);
+ b->root_lookup = lookup_add(b, b->interface, b->protocol, b->flags, b->key);
+ assert(b->root_lookup);
+
+ n = lookup_go(b->root_lookup);
+
+ if (b->dead)
+ return;
+
+ if (n < 0) {
+ /* sending of the initial query failed */
+
+ avahi_server_set_errno(b->server, AVAHI_ERR_FAILURE);
+
+ b->callback(
+ b, b->interface, b->protocol, AVAHI_BROWSER_FAILURE, NULL,
+ b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST,
+ b->userdata);
+
+ browser_cancel(b);
+ return;
+ }
+
+ /* Tell the client that we're done with the cache */
+ b->callback(
+ b, b->interface, b->protocol, AVAHI_BROWSER_CACHE_EXHAUSTED, NULL,
+ b->flags & AVAHI_LOOKUP_USE_WIDE_AREA ? AVAHI_LOOKUP_RESULT_WIDE_AREA : AVAHI_LOOKUP_RESULT_MULTICAST,
+ b->userdata);
+
+ if (!b->dead && b->root_lookup && b->root_lookup->flags & AVAHI_LOOKUP_USE_WIDE_AREA && n > 0) {
+
+ /* If we do wide area lookups and the the cache contained
+ * entries, we assume that it is complete, and tell the user
+ * so by firing ALL_FOR_NOW. */
+
+ b->callback(b, b->interface, b->protocol, AVAHI_BROWSER_ALL_FOR_NOW, NULL, AVAHI_LOOKUP_RESULT_WIDE_AREA, b->userdata);
+ }
+}
+
+void avahi_s_record_browser_restart(AvahiSRecordBrowser *b) {
+ assert(b);
+ assert(!b->dead);
+
+ browser_cancel(b);
+
+ /* Request a new iteration of the cache scanning */
+ if (!b->defer_time_event) {
+ b->defer_time_event = avahi_time_event_new(b->server->time_event_queue, NULL, defer_callback, b);
+ assert(b->defer_time_event);
+ }
+}
+
+AvahiSRecordBrowser *avahi_s_record_browser_new(
+ AvahiServer *server,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiKey *key,
+ AvahiLookupFlags flags,
+ AvahiSRecordBrowserCallback callback,
+ void* userdata) {
+
+ AvahiSRecordBrowser *b;
+
+ assert(server);
+ assert(key);
+ assert(callback);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !avahi_key_is_pattern(key), AVAHI_ERR_IS_PATTERN);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_key_is_valid(key), AVAHI_ERR_INVALID_KEY);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !(flags & AVAHI_LOOKUP_USE_WIDE_AREA) || !(flags & AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+ if (!(b = avahi_new(AvahiSRecordBrowser, 1))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ b->dead = 0;
+ b->server = server;
+ b->interface = interface;
+ b->protocol = protocol;
+ b->key = avahi_key_ref(key);
+ b->flags = flags;
+ b->callback = callback;
+ b->userdata = userdata;
+ b->n_lookups = 0;
+ AVAHI_LLIST_HEAD_INIT(AvahiSRBLookup, b->lookups);
+ b->root_lookup = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiSRecordBrowser, browser, server->record_browsers, b);
+
+ /* 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_free(AvahiSRecordBrowser *b) {
+ assert(b);
+ assert(!b->dead);
+
+ b->dead = 1;
+ b->server->need_browser_cleanup = 1;
+
+ browser_cancel(b);
+}
+
+void avahi_s_record_browser_destroy(AvahiSRecordBrowser *b) {
+ assert(b);
+
+ browser_cancel(b);
+
+ AVAHI_LLIST_REMOVE(AvahiSRecordBrowser, browser, b->server->record_browsers, b);
+
+ avahi_key_unref(b->key);
+
+ avahi_free(b);
+}
+
+void avahi_browser_cleanup(AvahiServer *server) {
+ AvahiSRecordBrowser *b;
+ AvahiSRecordBrowser *n;
+
+ assert(server);
+
+ while (server->need_browser_cleanup) {
+ server->need_browser_cleanup = 0;
+
+ for (b = server->record_browsers; b; b = n) {
+ n = b->browser_next;
+
+ if (b->dead)
+ avahi_s_record_browser_destroy(b);
+ }
+ }
+
+ if (server->wide_area_lookup_engine)
+ avahi_wide_area_cleanup(server->wide_area_lookup_engine);
+ avahi_multicast_lookup_engine_cleanup(server->multicast_lookup_engine);
+}
+
diff --git a/trunk/avahi-core/browse.h b/trunk/avahi-core/browse.h
new file mode 100644
index 0000000..36d4e2e
--- /dev/null
+++ b/trunk/avahi-core/browse.h
@@ -0,0 +1,62 @@
+#ifndef foobrowsehfoo
+#define foobrowsehfoo
+
+/* $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 <avahi-common/llist.h>
+
+#include "core.h"
+#include "timeeventq.h"
+#include "internal.h"
+#include "dns.h"
+#include "lookup.h"
+
+typedef struct AvahiSRBLookup AvahiSRBLookup;
+
+struct AvahiSRecordBrowser {
+ AVAHI_LLIST_FIELDS(AvahiSRecordBrowser, browser);
+ int dead;
+ AvahiServer *server;
+
+ 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);
+
+#endif
diff --git a/trunk/avahi-core/cache.c b/trunk/avahi-core/cache.c
new file mode 100644
index 0000000..4ba88b5
--- /dev/null
+++ b/trunk/avahi-core/cache.c
@@ -0,0 +1,508 @@
+/* $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 <string.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+
+#include "cache.h"
+#include "log.h"
+#include "rr-util.h"
+
+#define AVAHI_CACHE_ENTRIES_MAX 500
+
+static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) {
+ AvahiCacheEntry *t;
+
+ assert(c);
+ assert(e);
+
+/* avahi_log_debug("removing from cache: %p %p", c, e); */
+
+ /* Remove from hash table */
+ t = avahi_hashmap_lookup(c->hashmap, e->record->key);
+ AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e);
+ if (t)
+ avahi_hashmap_replace(c->hashmap, t->record->key, t);
+ else
+ avahi_hashmap_remove(c->hashmap, e->record->key);
+
+ /* Remove from linked list */
+ AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e);
+
+ if (e->time_event)
+ avahi_time_event_free(e->time_event);
+
+ 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);
+
+ assert(c->n_entries-- >= 1);
+}
+
+AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *iface) {
+ AvahiCache *c;
+ assert(server);
+
+ if (!(c = avahi_new(AvahiCache, 1))) {
+ avahi_log_error(__FILE__": Out of memory.");
+ return NULL; /* OOM */
+ }
+
+ c->server = server;
+ c->interface = iface;
+
+ if (!(c->hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL))) {
+ avahi_log_error(__FILE__": Out of memory.");
+ avahi_free(c);
+ return NULL; /* OOM */
+ }
+
+ AVAHI_LLIST_HEAD_INIT(AvahiCacheEntry, c->entries);
+ c->n_entries = 0;
+
+ c->last_rand_timestamp = 0;
+
+ return c;
+}
+
+void avahi_cache_free(AvahiCache *c) {
+ assert(c);
+
+ while (c->entries)
+ remove_entry(c, c->entries);
+ assert(c->n_entries == 0);
+
+ avahi_hashmap_free(c->hashmap);
+
+ avahi_free(c);
+}
+
+static AvahiCacheEntry *lookup_key(AvahiCache *c, AvahiKey *k) {
+ assert(c);
+ assert(k);
+
+ assert(!avahi_key_is_pattern(k));
+
+ return avahi_hashmap_lookup(c->hashmap, k);
+}
+
+void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata) {
+ void* ret;
+
+ assert(c);
+ assert(pattern);
+ assert(cb);
+
+ if (avahi_key_is_pattern(pattern)) {
+ AvahiCacheEntry *e, *n;
+
+ for (e = c->entries; e; e = n) {
+ n = e->entry_next;
+
+ if (avahi_key_pattern_match(pattern, e->record->key))
+ if ((ret = cb(c, pattern, e, userdata)))
+ return ret;
+ }
+
+ } else {
+ AvahiCacheEntry *e, *n;
+
+ for (e = lookup_key(c, pattern); e; e = n) {
+ n = e->by_key_next;
+
+ if ((ret = cb(c, pattern, e, userdata)))
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
+static void* lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) {
+ assert(c);
+ assert(pattern);
+ assert(e);
+
+ if (avahi_record_equal_no_ttl(e->record, userdata))
+ return e;
+
+ return NULL;
+}
+
+static AvahiCacheEntry *lookup_record(AvahiCache *c, AvahiRecord *r) {
+ assert(c);
+ assert(r);
+
+ return avahi_cache_walk(c, r->key, lookup_record_callback, r);
+}
+
+static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent);
+
+static void elapse_func(AvahiTimeEvent *t, void *userdata) {
+ AvahiCacheEntry *e = userdata;
+/* char *txt; */
+ unsigned percent = 0;
+
+ assert(t);
+ assert(e);
+
+/* txt = avahi_record_to_string(e->record); */
+
+ switch (e->state) {
+
+ case AVAHI_CACHE_EXPIRY_FINAL:
+ case AVAHI_CACHE_POOF_FINAL:
+ case AVAHI_CACHE_GOODBYE_FINAL:
+ case AVAHI_CACHE_REPLACE_FINAL:
+
+ remove_entry(e->cache, e);
+
+ e = NULL;
+/* avahi_log_debug("Removing entry from cache due to expiration (%s)", txt); */
+ break;
+
+ case AVAHI_CACHE_VALID:
+ case AVAHI_CACHE_POOF:
+ e->state = AVAHI_CACHE_EXPIRY1;
+ percent = 85;
+ break;
+
+ case AVAHI_CACHE_EXPIRY1:
+ e->state = AVAHI_CACHE_EXPIRY2;
+ percent = 90;
+ break;
+ case AVAHI_CACHE_EXPIRY2:
+ e->state = AVAHI_CACHE_EXPIRY3;
+ percent = 95;
+ break;
+
+ case AVAHI_CACHE_EXPIRY3:
+ e->state = AVAHI_CACHE_EXPIRY_FINAL;
+ percent = 100;
+ break;
+ }
+
+ if (e) {
+
+ assert(percent > 0);
+
+ /* Request a cache update if we are subscribed to this entry */
+ if (avahi_querier_shall_refresh_cache(e->cache->interface, e->record->key))
+ avahi_interface_post_query(e->cache->interface, e->record->key, 0, NULL);
+
+ /* Check again later */
+ next_expiry(e->cache, e, percent);
+
+ }
+
+/* avahi_free(txt); */
+}
+
+static void update_time_event(AvahiCache *c, AvahiCacheEntry *e) {
+ assert(c);
+ assert(e);
+
+ if (e->time_event)
+ avahi_time_event_update(e->time_event, &e->expiry);
+ else
+ e->time_event = avahi_time_event_new(c->server->time_event_queue, &e->expiry, elapse_func, e);
+}
+
+static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, unsigned percent) {
+ AvahiUsec usec, left, right;
+ time_t now;
+
+ assert(c);
+ assert(e);
+ assert(percent > 0 && percent <= 100);
+
+ usec = (AvahiUsec) e->record->ttl * 10000;
+
+ left = usec * percent;
+ right = usec * (percent+2); /* 2% jitter */
+
+ now = time(NULL);
+
+ if (now >= c->last_rand_timestamp + 10) {
+ c->last_rand = rand();
+ c->last_rand_timestamp = now;
+ }
+
+ usec = left + (AvahiUsec) ((double) (right-left) * c->last_rand / (RAND_MAX+1.0));
+
+ e->expiry = e->timestamp;
+ avahi_timeval_add(&e->expiry, usec);
+
+/* g_message("wake up in +%lu seconds", e->expiry.tv_sec - e->timestamp.tv_sec); */
+
+ update_time_event(c, e);
+}
+
+static void expire_in_one_second(AvahiCache *c, AvahiCacheEntry *e, AvahiCacheEntryState state) {
+ assert(c);
+ assert(e);
+
+ e->state = state;
+ gettimeofday(&e->expiry, NULL);
+ avahi_timeval_add(&e->expiry, 1000000); /* 1s */
+ update_time_event(c, e);
+}
+
+void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a) {
+/* char *txt; */
+
+ assert(c);
+ assert(r && r->ref >= 1);
+
+/* txt = avahi_record_to_string(r); */
+
+ if (r->ttl == 0) {
+ /* This is a goodbye request */
+
+ AvahiCacheEntry *e;
+
+ if ((e = lookup_record(c, r)))
+ expire_in_one_second(c, e, AVAHI_CACHE_GOODBYE_FINAL);
+
+ } else {
+ AvahiCacheEntry *e = NULL, *first;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ /* This is an update request */
+
+ if ((first = lookup_key(c, r->key))) {
+
+ if (cache_flush) {
+
+ /* For unique entries drop all entries older than one second */
+ for (e = first; e; e = e->by_key_next) {
+ AvahiUsec t;
+
+ t = avahi_timeval_diff(&now, &e->timestamp);
+
+ if (t > 1000000)
+ expire_in_one_second(c, e, AVAHI_CACHE_REPLACE_FINAL);
+ }
+ }
+
+ /* Look for exactly the same entry */
+ for (e = first; e; e = e->by_key_next)
+ if (avahi_record_equal_no_ttl(e->record, r))
+ break;
+ }
+
+ if (e) {
+
+/* avahi_log_debug("found matching cache entry"); */
+
+ /* We need to update the hash table key if we replace the
+ * record */
+ if (e->by_key_prev == NULL)
+ avahi_hashmap_replace(c->hashmap, r->key, e);
+
+ /* Update the record */
+ avahi_record_unref(e->record);
+ e->record = avahi_record_ref(r);
+
+/* avahi_log_debug("cache: updating %s", txt); */
+
+ } else {
+ /* No entry found, therefore we create a new one */
+
+/* avahi_log_debug("cache: couldn't find matching cache entry for %s", txt); */
+
+ if (c->n_entries >= AVAHI_CACHE_ENTRIES_MAX)
+ return;
+
+ if (!(e = avahi_new(AvahiCacheEntry, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ return;
+ }
+
+ e->cache = c;
+ e->time_event = NULL;
+ e->record = avahi_record_ref(r);
+
+ /* Append to hash table */
+ AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, first, e);
+ avahi_hashmap_replace(c->hashmap, e->record->key, first);
+
+ /* Append to linked list */
+ AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e);
+
+ c->n_entries++;
+
+ /* Notify subscribers */
+ avahi_multicast_lookup_engine_notify(c->server->multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_NEW);
+ }
+
+ e->origin = *a;
+ e->timestamp = now;
+ next_expiry(c, e, 80);
+ e->state = AVAHI_CACHE_VALID;
+ e->cache_flush = cache_flush;
+ }
+
+/* avahi_free(txt); */
+}
+
+struct dump_data {
+ AvahiDumpCallback callback;
+ void* userdata;
+};
+
+static void dump_callback(void* key, void* data, void* userdata) {
+ AvahiCacheEntry *e = data;
+ AvahiKey *k = key;
+ struct dump_data *dump_data = userdata;
+
+ assert(k);
+ assert(e);
+ assert(data);
+
+ for (; e; e = e->by_key_next) {
+ char *t;
+
+ if (!(t = avahi_record_to_string(e->record)))
+ continue; /* OOM */
+
+ dump_data->callback(t, dump_data->userdata);
+ avahi_free(t);
+ }
+}
+
+int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata) {
+ struct dump_data data;
+
+ assert(c);
+ assert(callback);
+
+ callback(";;; CACHE DUMP FOLLOWS ;;;", userdata);
+
+ data.callback = callback;
+ data.userdata = userdata;
+
+ avahi_hashmap_foreach(c->hashmap, dump_callback, &data);
+
+ return 0;
+}
+
+int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) {
+ struct timeval now;
+ unsigned age;
+
+ assert(c);
+ assert(e);
+
+ gettimeofday(&now, NULL);
+
+ age = (unsigned) (avahi_timeval_diff(&now, &e->timestamp)/1000000);
+
+/* avahi_log_debug("age: %lli, ttl/2: %u", age, e->record->ttl); */
+
+ return age >= e->record->ttl/2;
+}
+
+void avahi_cache_flush(AvahiCache *c) {
+ assert(c);
+
+ while (c->entries)
+ remove_entry(c, c->entries);
+}
+
+/*** Passive observation of failure ***/
+
+static void* start_poof_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) {
+ AvahiAddress *a = userdata;
+
+ assert(c);
+ assert(pattern);
+ assert(e);
+ assert(a);
+
+ switch (e->state) {
+ case AVAHI_CACHE_VALID:
+
+ /* The entry was perfectly valid till, now, so let's enter
+ * POOF mode */
+
+ e->state = AVAHI_CACHE_POOF;
+ e->poof_address = *a;
+
+ break;
+
+ case AVAHI_CACHE_POOF:
+
+ /* This is the second time we got no response, so let's
+ * fucking remove this entry. */
+
+ expire_in_one_second(c, e, AVAHI_CACHE_POOF_FINAL);
+ break;
+
+ default:
+ ;
+ }
+
+ return NULL;
+}
+
+void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a) {
+ assert(c);
+ assert(key);
+
+ avahi_cache_walk(c, key, start_poof_callback, (void*) a);
+}
+
+void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a) {
+ AvahiCacheEntry *e;
+
+ assert(c);
+ assert(record);
+ assert(a);
+
+ if (!(e = lookup_record(c, record)))
+ return;
+
+ /* This function is called for each response suppression
+ record. If the matching cache entry is in POOF state and the
+ query address is the same, we put it back into valid mode */
+
+ if (e->state == AVAHI_CACHE_POOF || e->state == AVAHI_CACHE_POOF_FINAL)
+ if (avahi_address_cmp(a, &e->poof_address) == 0) {
+ e->state = AVAHI_CACHE_VALID;
+ next_expiry(c, e, 80);
+ }
+}
+
+
+
diff --git a/trunk/avahi-core/cache.h b/trunk/avahi-core/cache.h
new file mode 100644
index 0000000..edf9fa5
--- /dev/null
+++ b/trunk/avahi-core/cache.h
@@ -0,0 +1,101 @@
+#ifndef foocachehfoo
+#define foocachehfoo
+
+/* $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 AvahiCache AvahiCache;
+
+#include <avahi-common/llist.h>
+#include "prioq.h"
+#include "internal.h"
+#include "timeeventq.h"
+#include "hashmap.h"
+
+typedef enum {
+ AVAHI_CACHE_VALID,
+ AVAHI_CACHE_EXPIRY1,
+ AVAHI_CACHE_EXPIRY2,
+ AVAHI_CACHE_EXPIRY3,
+ AVAHI_CACHE_EXPIRY_FINAL,
+ AVAHI_CACHE_POOF, /* Passive observation of failure */
+ AVAHI_CACHE_POOF_FINAL,
+ AVAHI_CACHE_GOODBYE_FINAL,
+ AVAHI_CACHE_REPLACE_FINAL
+} AvahiCacheEntryState;
+
+typedef struct AvahiCacheEntry AvahiCacheEntry;
+
+struct AvahiCacheEntry {
+ AvahiCache *cache;
+ AvahiRecord *record;
+ struct timeval timestamp;
+ struct timeval expiry;
+ int cache_flush;
+
+ AvahiAddress origin;
+
+ AvahiCacheEntryState state;
+ AvahiTimeEvent *time_event;
+
+ AvahiAddress poof_address;
+
+ AVAHI_LLIST_FIELDS(AvahiCacheEntry, by_key);
+ AVAHI_LLIST_FIELDS(AvahiCacheEntry, entry);
+};
+
+struct AvahiCache {
+ AvahiServer *server;
+
+ AvahiInterface *interface;
+
+ AvahiHashmap *hashmap;
+
+ AVAHI_LLIST_HEAD(AvahiCacheEntry, entries);
+
+ unsigned n_entries;
+
+ int last_rand;
+ time_t last_rand_timestamp;
+};
+
+AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *interface);
+void avahi_cache_free(AvahiCache *c);
+
+void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a);
+
+int avahi_cache_dump(AvahiCache *c, AvahiDumpCallback callback, void* userdata);
+
+typedef void* AvahiCacheWalkCallback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata);
+void* avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, void* userdata);
+
+int avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e);
+
+/** Start the "Passive observation of Failure" algorithm for all
+ * records of the specified key. The specified address is */
+void avahi_cache_start_poof(AvahiCache *c, AvahiKey *key, const AvahiAddress *a);
+
+/* Stop a previously started POOF algorithm for a record. (Used for response suppresions records */
+void avahi_cache_stop_poof(AvahiCache *c, AvahiRecord *record, const AvahiAddress *a);
+
+void avahi_cache_flush(AvahiCache *c);
+
+#endif
diff --git a/trunk/avahi-core/conformance-test.c b/trunk/avahi-core/conformance-test.c
new file mode 100644
index 0000000..dc3c864
--- /dev/null
+++ b/trunk/avahi-core/conformance-test.c
@@ -0,0 +1,160 @@
+/* $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 <unistd.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <avahi-common/alternative.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/timeval.h>
+
+#include <avahi-core/core.h>
+#include <avahi-core/log.h>
+#include <avahi-core/lookup.h>
+#include <avahi-core/publish.h>
+
+static char *name = NULL;
+static AvahiSEntryGroup *group = NULL;
+static int try = 0;
+static AvahiServer *avahi = NULL;
+static const AvahiPoll *poll_api;
+
+static void dump_line(const char *text, AVAHI_GCC_UNUSED void* userdata) {
+ printf("%s\n", text);
+}
+
+static void dump_timeout_callback(AvahiTimeout *timeout, AVAHI_GCC_UNUSED void* userdata) {
+ struct timeval tv;
+
+ avahi_server_dump(avahi, dump_line, NULL);
+
+ avahi_elapse_time(&tv, 5000, 0);
+ poll_api->timeout_update(timeout, &tv);
+}
+
+static void entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void* userdata);
+
+static void create_service(const char *t) {
+ char *n;
+
+ assert(t || name);
+
+ n = t ? avahi_strdup(t) : avahi_alternative_service_name(name);
+ avahi_free(name);
+ name = n;
+
+ if (group)
+ avahi_s_entry_group_reset(group);
+ else
+ group = avahi_s_entry_group_new(avahi, entry_group_callback, NULL);
+
+ avahi_server_add_service(avahi, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, name, "_http._tcp", NULL, NULL, 80, "foo", NULL);
+ avahi_s_entry_group_commit(group);
+
+ try++;
+}
+
+static void rename_timeout_callback(AvahiTimeout *timeout, AVAHI_GCC_UNUSED void *userdata) {
+ struct timeval tv;
+
+ if (access("flag", F_OK) == 0) {
+ create_service("New - Bonjour Service Name");
+ return;
+ }
+
+ avahi_elapse_time(&tv, 5000, 0);
+ poll_api->timeout_update(timeout, &tv);
+}
+
+static void entry_group_callback(AVAHI_GCC_UNUSED AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void* userdata) {
+ if (state == AVAHI_ENTRY_GROUP_COLLISION)
+ create_service(NULL);
+ else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
+ avahi_log_debug("ESTABLISHED !!!!");
+ try = 0;
+ }
+}
+
+static void server_callback(AvahiServer *s, AvahiServerState state, AVAHI_GCC_UNUSED void* userdata) {
+ avahi_log_debug("server state: %i", state);
+
+ if (state == AVAHI_SERVER_RUNNING) {
+ avahi_server_dump(avahi, dump_line, NULL);
+ } else if (state == AVAHI_SERVER_COLLISION) {
+ char *n;
+
+ n = avahi_alternative_host_name(avahi_server_get_host_name(s));
+ avahi_log_warn("Host name conflict, retrying with <%s>", n);
+ avahi_server_set_host_name(s, n);
+ avahi_free(n);
+
+ }
+}
+
+int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) {
+ int error;
+ AvahiSimplePoll *simple_poll;
+ struct timeval tv;
+ struct AvahiServerConfig config;
+
+ simple_poll = avahi_simple_poll_new();
+ poll_api = avahi_simple_poll_get(simple_poll);
+
+ avahi_server_config_init(&config);
+ config.publish_workstation = 0;
+ config.use_ipv6 = 0;
+ config.publish_domain = 0;
+ config.publish_hinfo = 0;
+ avahi = avahi_server_new(poll_api, &config, server_callback, NULL, &error);
+ avahi_server_config_free(&config);
+
+ avahi_elapse_time(&tv, 5000, 0);
+ poll_api->timeout_new(poll_api, &tv, dump_timeout_callback, avahi);
+
+ avahi_elapse_time(&tv, 5000, 0);
+ poll_api->timeout_new(poll_api, &tv, rename_timeout_callback, avahi);
+
+ /* Evil, but the conformace test requires that*/
+ create_service("gurke");
+
+
+ avahi_simple_poll_loop(simple_poll);
+
+ if (group)
+ avahi_s_entry_group_free(group);
+ avahi_server_free(avahi);
+
+ avahi_simple_poll_free(simple_poll);
+
+ return 0;
+}
diff --git a/trunk/avahi-core/core.h b/trunk/avahi-core/core.h
new file mode 100644
index 0000000..e73abd2
--- /dev/null
+++ b/trunk/avahi-core/core.h
@@ -0,0 +1,153 @@
+#ifndef foocorehfoo
+#define foocorehfoo
+
+/* $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 core.h The Avahi Multicast DNS and DNS Service Discovery implementation. */
+
+/** An mDNS responder object */
+typedef struct AvahiServer AvahiServer;
+
+#include <avahi-common/cdecl.h>
+#include <avahi-common/address.h>
+#include <avahi-common/defs.h>
+#include <avahi-common/watch.h>
+#include <avahi-core/rr.h>
+
+AVAHI_C_DECL_BEGIN
+
+/** Maximum number of defined DNS servers for wide area DNS */
+#define AVAHI_WIDE_AREA_SERVERS_MAX 4
+
+/** Prototype for callback functions which are called whenever the state of an AvahiServer object changes */
+typedef void (*AvahiServerCallback) (AvahiServer *s, AvahiServerState 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 */
+ 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 */
+ int publish_addresses; /**< Register A, AAAA and PTR records for all local IP addresses */
+ int publish_workstation; /**< Register a _workstation._tcp service */
+ int publish_domain; /**< Announce the local domain for browsing */
+ int check_response_ttl; /**< If enabled the server ignores all incoming responses with IP TTL != 255. Newer versions of the RFC do no longer contain this check, so it is disabled by default. */
+ int use_iff_running; /**< Require IFF_RUNNING on local network interfaces. This is the official way to check for link beat. Unfortunately this doesn't work with all drivers. So bettere leave this off. */
+ 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_WIDE_AREA_SERVERS_MAX]; /** Unicast DNS server to use for wide area lookup */
+ unsigned n_wide_area_servers; /**< Number of servers in wide_area_servers[] */
+ int disallow_other_stacks; /**< Make sure that only one mDNS responder is run at the same time on the local machine. If this is enable Avahi will not set SO_REUSADDR on its sockets, effectively preventing other stacks from running on the local machine */
+ AvahiStringList *browse_domains; /**< Additional browsing domains */
+ int disable_publishing; /**< Disable publishing of any record */
+ int allow_point_to_point; /**< Enable publishing on POINTOPOINT interfaces */
+} AvahiServerConfig;
+
+/** Allocate a new mDNS responder object. */
+AvahiServer *avahi_server_new(
+ const AvahiPoll *api, /**< The main loop adapter */
+ const AvahiServerConfig *sc, /**< If non-NULL a pointer to a configuration structure for the server. The server makes an internal deep copy of this structure, so you may free it using avahi_server_config_done() immediately after calling this function. */
+ AvahiServerCallback callback, /**< A callback which is called whenever the state of the server changes */
+ void* userdata, /**< An opaque pointer which is passed to the callback function */
+ int *error);
+
+/** Free an mDNS responder object */
+void avahi_server_free(AvahiServer* s);
+
+/** Fill in default values for a server configuration structure. If you
+ * make use of an AvahiServerConfig structure be sure to initialize
+ * it with this function for the sake of upwards library
+ * compatibility. This call may allocate strings on the heap. To
+ * release this memory make sure to call
+ * avahi_server_config_done(). If you want to replace any strings in
+ * the structure be sure to free the strings filled in by this
+ * function with avahi_free() first and allocate the replacements with
+ * g_malloc() (or g_strdup()).*/
+AvahiServerConfig* avahi_server_config_init(
+ AvahiServerConfig *c /**< A structure which shall be filled in */ );
+
+/** Make a deep copy of the configuration structure *c to *ret. */
+AvahiServerConfig* avahi_server_config_copy(
+ AvahiServerConfig *ret /**< destination */,
+ const AvahiServerConfig *c /**< source */);
+
+/** Free the data in a server configuration structure. */
+void avahi_server_config_free(AvahiServerConfig *c);
+
+/** Return the currently chosen domain name of the server object. The
+ * return value points to an internally allocated string. Be sure to
+ * make a copy of the string before calling any other library
+ * functions. */
+const char* avahi_server_get_domain_name(AvahiServer *s);
+
+/** Return the currently chosen host name. The return value points to a internally allocated string. */
+const char* avahi_server_get_host_name(AvahiServer *s);
+
+/** Return the currently chosen host name as a FQDN ("fully qualified
+ * domain name", i.e. the concatenation of the host and domain
+ * name). The return value points to a internally allocated string. */
+const char* avahi_server_get_host_name_fqdn(AvahiServer *s);
+
+/** Change the host name of a running mDNS responder. This will drop
+all automicatilly generated RRs and readd them with the new
+name. Since the responder has to probe for the new RRs this function
+takes some time to take effect altough it returns immediately. This
+function is intended to be called when a host name conflict is
+reported using AvahiServerCallback. The caller should readd all user
+defined RRs too since they otherwise continue to point to the outdated
+host name..*/
+int avahi_server_set_host_name(AvahiServer *s, const char *host_name);
+
+/** Change the domain name of a running mDNS responder. The same rules
+ * as with avahi_server_set_host_name() apply. */
+int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name);
+
+/** Return the opaque user data pointer attached to a server object */
+void* avahi_server_get_data(AvahiServer *s);
+
+/** Change the opaque user data pointer attached to a server object */
+void avahi_server_set_data(AvahiServer *s, void* userdata);
+
+/** Return the current state of the server object */
+AvahiServerState avahi_server_get_state(AvahiServer *s);
+
+/** 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);
+
+/** Return the last error code */
+int avahi_server_errno(AvahiServer *s);
+
+/** Return the local service cookie */
+uint32_t avahi_server_get_local_service_cookie(AvahiServer *s);
+
+/** Set the wide area DNS servers */
+int avahi_server_set_wide_area_servers(AvahiServer *s, const AvahiAddress *a, unsigned n);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/dns-srv-rr.h b/trunk/avahi-core/dns-srv-rr.h
new file mode 100644
index 0000000..1a7f95f
--- /dev/null
+++ b/trunk/avahi-core/dns-srv-rr.h
@@ -0,0 +1,89 @@
+#ifndef foodnssrvhfoo
+#define foodnssrvhfoo
+
+/* $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 avahi-core/dns-srv-rr.h Functions for announcing and browsing for unicast DNS servers via mDNS */
+
+/** 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;
+
+#include <avahi-common/cdecl.h>
+#include <avahi-common/defs.h>
+#include <avahi-core/core.h>
+#include <avahi-core/publish.h>
+
+AVAHI_C_DECL_BEGIN
+
+/** 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)*/
+ AVAHI_DNS_SERVER_MAX
+} 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,
+ AvahiPublishFlags flags,
+ const char *domain,
+ AvahiDNSServerType type,
+ const AvahiAddress *address,
+ uint16_t port /** should be 53 */);
+
+/** 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);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/dns-test.c b/trunk/avahi-core/dns-test.c
new file mode 100644
index 0000000..ae2343d
--- /dev/null
+++ b/trunk/avahi-core/dns-test.c
@@ -0,0 +1,115 @@
+/* $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 <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/defs.h>
+#include <avahi-common/malloc.h>
+
+#include "dns.h"
+#include "log.h"
+#include "util.h"
+
+int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) {
+ char t[AVAHI_DOMAIN_NAME_MAX], *m;
+ const char *a, *b, *c, *d;
+ AvahiDnsPacket *p;
+ AvahiRecord *r, *r2;
+ uint8_t rdata[AVAHI_DNS_RDATA_MAX];
+ size_t l;
+
+ p = avahi_dns_packet_new(0);
+
+ assert(avahi_dns_packet_append_name(p, a = "Ahello.hello.hello.de."));
+ assert(avahi_dns_packet_append_name(p, b = "Bthis is a test.hello.de."));
+ assert(avahi_dns_packet_append_name(p, c = "Cthis\\.is\\.a\\.test\\.with\\.dots.hello.de."));
+ assert(avahi_dns_packet_append_name(p, d = "Dthis\\\\is another test.hello.de."));
+
+ avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), p->size);
+
+ assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0);
+ avahi_log_debug(">%s<", t);
+ assert(avahi_domain_equal(a, t));
+
+ assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0);
+ avahi_log_debug(">%s<", t);
+ assert(avahi_domain_equal(b, t));
+
+ assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0);
+ avahi_log_debug(">%s<", t);
+ assert(avahi_domain_equal(c, t));
+
+ assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0);
+ avahi_log_debug(">%s<", t);
+ assert(avahi_domain_equal(d, t));
+
+ avahi_dns_packet_free(p);
+
+ /* RDATA PARSING AND SERIALIZATION */
+
+ /* Create an AvahiRecord with some usful data */
+ r = avahi_record_new_full("foobar.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL);
+ assert(r);
+ r->data.hinfo.cpu = avahi_strdup("FOO");
+ r->data.hinfo.os = avahi_strdup("BAR");
+
+ /* Serialize it into a blob */
+ assert((l = avahi_rdata_serialize(r, rdata, sizeof(rdata))) != (size_t) -1);
+
+ /* Print it */
+ avahi_hexdump(rdata, l);
+
+ /* Create a new record and fill in the data from the blob */
+ r2 = avahi_record_new(r->key, AVAHI_DEFAULT_TTL);
+ assert(r2);
+ assert(avahi_rdata_parse(r2, rdata, l) >= 0);
+
+ /* Compare both versions */
+ assert(avahi_record_equal_no_ttl(r, r2));
+
+ /* Free the records */
+ avahi_record_unref(r);
+ avahi_record_unref(r2);
+
+ r = avahi_record_new_full("foobar", 77, 77, AVAHI_DEFAULT_TTL);
+ assert(r);
+
+ assert(r->data.generic.data = avahi_memdup("HALLO", r->data.generic.size = 5));
+
+ m = avahi_record_to_string(r);
+ assert(m);
+
+ avahi_log_debug(">%s<", m);
+
+ avahi_free(m);
+ avahi_record_unref(r);
+
+ return 0;
+}
diff --git a/trunk/avahi-core/dns.c b/trunk/avahi-core/dns.c
new file mode 100644
index 0000000..b31aa20
--- /dev/null
+++ b/trunk/avahi-core/dns.c
@@ -0,0 +1,856 @@
+/* $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 <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <netinet/in.h>
+
+#include <avahi-common/defs.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include "dns.h"
+#include "log.h"
+
+AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu) {
+ AvahiDnsPacket *p;
+ size_t max_size;
+
+ if (mtu <= 0)
+ max_size = AVAHI_DNS_PACKET_SIZE_MAX;
+ else if (mtu >= AVAHI_DNS_PACKET_EXTRA_SIZE)
+ max_size = mtu - AVAHI_DNS_PACKET_EXTRA_SIZE;
+ else
+ max_size = 0;
+
+ if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE)
+ max_size = AVAHI_DNS_PACKET_HEADER_SIZE;
+
+ if (!(p = avahi_malloc(sizeof(AvahiDnsPacket) + max_size)))
+ return p;
+
+ p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE;
+ p->max_size = max_size;
+ p->name_table = NULL;
+ p->data = NULL;
+
+ memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size);
+ return p;
+}
+
+AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu) {
+ AvahiDnsPacket *p;
+
+ if (!(p = avahi_dns_packet_new(mtu)))
+ return NULL;
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ return p;
+}
+
+AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa) {
+ AvahiDnsPacket *p;
+
+ if (!(p = avahi_dns_packet_new(mtu)))
+ return NULL;
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, aa, 0, 0, 0, 0, 0, 0, 0));
+ return p;
+}
+
+AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa) {
+ AvahiDnsPacket *r;
+ assert(p);
+
+ if (!(r = avahi_dns_packet_new_response(mtu, aa)))
+ return NULL;
+
+ if (copy_queries) {
+ unsigned saved_rindex;
+ uint32_t n;
+
+ saved_rindex = p->rindex;
+ p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE;
+
+ for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n--) {
+ AvahiKey *k;
+ int unicast_response;
+
+ if ((k = avahi_dns_packet_consume_key(p, &unicast_response))) {
+ avahi_dns_packet_append_key(r, k, unicast_response);
+ avahi_key_unref(k);
+ }
+ }
+
+ p->rindex = saved_rindex;
+
+ avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_QDCOUNT, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT));
+ }
+
+ avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_ID, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID));
+
+ avahi_dns_packet_set_field(r, AVAHI_DNS_FIELD_FLAGS,
+ (avahi_dns_packet_get_field(r, AVAHI_DNS_FIELD_FLAGS) & ~AVAHI_DNS_FLAG_OPCODE) |
+ (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_OPCODE));
+
+ return r;
+}
+
+
+void avahi_dns_packet_free(AvahiDnsPacket *p) {
+ assert(p);
+
+ if (p->name_table)
+ avahi_hashmap_free(p->name_table);
+
+ avahi_free(p);
+}
+
+void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v) {
+ assert(p);
+ assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE);
+
+ ((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx] = htons(v);
+}
+
+uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx) {
+ assert(p);
+ assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE);
+
+ return ntohs(((uint16_t*) AVAHI_DNS_PACKET_DATA(p))[idx]);
+}
+
+void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx) {
+ assert(p);
+ assert(idx < AVAHI_DNS_PACKET_HEADER_SIZE);
+
+ avahi_dns_packet_set_field(p, idx, avahi_dns_packet_get_field(p, idx) + 1);
+}
+
+uint8_t* avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name) {
+ uint8_t *d, *saved_ptr = NULL;
+ size_t saved_size;
+
+ assert(p);
+ assert(name);
+
+ saved_size = p->size;
+ saved_ptr = avahi_dns_packet_extend(p, 0);
+
+ while (*name) {
+ uint8_t* prev;
+ const char *pname;
+ char label[64], *u;
+
+ /* Check whether we can compress this name. */
+
+ if (p->name_table && (prev = avahi_hashmap_lookup(p->name_table, name))) {
+ unsigned idx;
+
+ assert(prev >= AVAHI_DNS_PACKET_DATA(p));
+ idx = (unsigned) (prev - AVAHI_DNS_PACKET_DATA(p));
+
+ assert(idx < p->size);
+
+ if (idx < 0x4000) {
+ uint8_t *t;
+ if (!(t = (uint8_t*) avahi_dns_packet_extend(p, sizeof(uint16_t))))
+ return NULL;
+
+ t[0] = (uint8_t) ((0xC000 | idx) >> 8);
+ t[1] = (uint8_t) idx;
+ return saved_ptr;
+ }
+ }
+
+ pname = name;
+
+ if (!(avahi_unescape_label(&name, label, sizeof(label))))
+ goto fail;
+
+ if (!(d = avahi_dns_packet_append_string(p, label)))
+ goto fail;
+
+ if (!p->name_table)
+ /* This works only for normalized domain names */
+ p->name_table = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, NULL);
+
+ if (!(u = avahi_strdup(pname)))
+ avahi_log_error("avahi_strdup() failed.");
+ else
+ avahi_hashmap_insert(p->name_table, u, d);
+ }
+
+ if (!(d = avahi_dns_packet_extend(p, 1)))
+ goto fail;
+
+ *d = 0;
+
+ return saved_ptr;
+
+fail:
+ p->size = saved_size;
+ return NULL;
+}
+
+uint8_t* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v) {
+ uint8_t *d;
+ assert(p);
+
+ if (!(d = avahi_dns_packet_extend(p, sizeof(uint16_t))))
+ return NULL;
+
+ d[0] = (uint8_t) (v >> 8);
+ d[1] = (uint8_t) v;
+ return d;
+}
+
+uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v) {
+ uint8_t *d;
+ assert(p);
+
+ if (!(d = avahi_dns_packet_extend(p, sizeof(uint32_t))))
+ return NULL;
+
+ d[0] = (uint8_t) (v >> 24);
+ d[1] = (uint8_t) (v >> 16);
+ d[2] = (uint8_t) (v >> 8);
+ d[3] = (uint8_t) v;
+
+ return d;
+}
+
+uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *b, size_t l) {
+ uint8_t* d;
+
+ assert(p);
+ assert(b);
+ assert(l);
+
+ if (!(d = avahi_dns_packet_extend(p, l)))
+ return NULL;
+
+ memcpy(d, b, l);
+ return d;
+}
+
+uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s) {
+ uint8_t* d;
+ size_t k;
+
+ assert(p);
+ assert(s);
+
+ if ((k = strlen(s)) >= 255)
+ k = 255;
+
+ if (!(d = avahi_dns_packet_extend(p, k+1)))
+ return NULL;
+
+ *d = (uint8_t) k;
+ memcpy(d+1, s, k);
+
+ return d;
+}
+
+uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l) {
+ uint8_t *d;
+
+ assert(p);
+
+ if (p->size+l > p->max_size)
+ return NULL;
+
+ d = AVAHI_DNS_PACKET_DATA(p) + p->size;
+ p->size += l;
+
+ return d;
+}
+
+int avahi_dns_packet_check_valid(AvahiDnsPacket *p) {
+ uint16_t flags;
+ assert(p);
+
+ 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 (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);
+
+ return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR);
+}
+
+static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_t l) {
+ int ret = 0;
+ int compressed = 0;
+ int first_label = 1;
+ unsigned label_ptr;
+ int i;
+ assert(p && ret_name && l);
+
+ for (i = 0; i < AVAHI_DNS_LABELS_MAX; i++) {
+ uint8_t n;
+
+ if (idx+1 > p->size)
+ return -1;
+
+ n = AVAHI_DNS_PACKET_DATA(p)[idx];
+
+ if (!n) {
+ idx++;
+ if (!compressed)
+ ret++;
+
+ if (l < 1)
+ return -1;
+ *ret_name = 0;
+
+ return ret;
+
+ } else if (n <= 63) {
+ /* Uncompressed label */
+ idx++;
+ if (!compressed)
+ ret++;
+
+ if (idx + n > p->size)
+ return -1;
+
+ if ((size_t) n + 1 > l)
+ return -1;
+
+ if (!first_label) {
+ *(ret_name++) = '.';
+ l--;
+ } else
+ first_label = 0;
+
+ if (!(avahi_escape_label((char*) AVAHI_DNS_PACKET_DATA(p) + idx, n, &ret_name, &l)))
+ return -1;
+
+ idx += n;
+
+ if (!compressed)
+ ret += n;
+ } else if ((n & 0xC0) == 0xC0) {
+ /* Compressed label */
+
+ if (idx+2 > p->size)
+ return -1;
+
+ label_ptr = ((unsigned) (AVAHI_DNS_PACKET_DATA(p)[idx] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[idx+1];
+
+ if ((label_ptr < AVAHI_DNS_PACKET_HEADER_SIZE) || (label_ptr >= idx))
+ return -1;
+
+ idx = label_ptr;
+
+ if (!compressed)
+ ret += 2;
+
+ compressed = 1;
+ } else
+ return -1;
+ }
+}
+
+int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l) {
+ int r;
+
+ if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0)
+ return -1;
+
+ p->rindex += r;
+ return 0;
+}
+
+int avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, uint16_t *ret_v) {
+ uint8_t *d;
+
+ assert(p);
+ assert(ret_v);
+
+ if (p->rindex + sizeof(uint16_t) > p->size)
+ return -1;
+
+ d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex);
+ *ret_v = (d[0] << 8) | d[1];
+ p->rindex += sizeof(uint16_t);
+
+ return 0;
+}
+
+int avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, uint32_t *ret_v) {
+ uint8_t* d;
+
+ assert(p);
+ assert(ret_v);
+
+ if (p->rindex + sizeof(uint32_t) > p->size)
+ return -1;
+
+ d = (uint8_t*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex);
+ *ret_v = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3];
+ p->rindex += sizeof(uint32_t);
+
+ return 0;
+}
+
+int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void * ret_data, size_t l) {
+ assert(p);
+ assert(ret_data);
+ assert(l > 0);
+
+ if (p->rindex + l > p->size)
+ return -1;
+
+ memcpy(ret_data, AVAHI_DNS_PACKET_DATA(p) + p->rindex, l);
+ p->rindex += l;
+
+ return 0;
+}
+
+int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l) {
+ size_t k;
+
+ assert(p);
+ assert(ret_string);
+ assert(l > 0);
+
+ if (p->rindex >= p->size)
+ return -1;
+
+ k = AVAHI_DNS_PACKET_DATA(p)[p->rindex];
+
+ if (p->rindex+1+k > p->size)
+ return -1;
+
+ if (l > k+1)
+ l = k+1;
+
+ memcpy(ret_string, AVAHI_DNS_PACKET_DATA(p)+p->rindex+1, l-1);
+ ret_string[l-1] = 0;
+
+ p->rindex += 1+k;
+
+ return 0;
+}
+
+const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p) {
+ assert(p);
+
+ if (p->rindex > p->size)
+ return NULL;
+
+ return AVAHI_DNS_PACKET_DATA(p) + p->rindex;
+}
+
+int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length) {
+ assert(p);
+
+ if (p->rindex + length > p->size)
+ return -1;
+
+ p->rindex += length;
+ return 0;
+}
+
+static int parse_rdata(AvahiDnsPacket *p, AvahiRecord *r, uint16_t rdlength) {
+ char buf[AVAHI_DOMAIN_NAME_MAX];
+ const void* start;
+
+ assert(p);
+ assert(r);
+
+ start = avahi_dns_packet_get_rptr(p);
+
+ switch (r->key->type) {
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ case AVAHI_DNS_TYPE_NS:
+
+ if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
+ return -1;
+
+ r->data.ptr.name = avahi_strdup(buf);
+ break;
+
+
+ case AVAHI_DNS_TYPE_SRV:
+
+ if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 ||
+ avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 ||
+ avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 ||
+ avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
+ return -1;
+
+ r->data.srv.name = avahi_strdup(buf);
+ break;
+
+ case AVAHI_DNS_TYPE_HINFO:
+
+ if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
+ return -1;
+
+ r->data.hinfo.cpu = avahi_strdup(buf);
+
+ if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
+ return -1;
+
+ r->data.hinfo.os = avahi_strdup(buf);
+ break;
+
+ case AVAHI_DNS_TYPE_TXT:
+
+ if (rdlength > 0) {
+ if (avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength, &r->data.txt.string_list) < 0)
+ return -1;
+
+ if (avahi_dns_packet_skip(p, rdlength) < 0)
+ return -1;
+ } else
+ r->data.txt.string_list = NULL;
+
+ break;
+
+ case AVAHI_DNS_TYPE_A:
+
+/* avahi_log_debug("A"); */
+
+ if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0)
+ return -1;
+
+ break;
+
+ case AVAHI_DNS_TYPE_AAAA:
+
+/* avahi_log_debug("aaaa"); */
+
+ if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0)
+ return -1;
+
+ break;
+
+ default:
+
+/* avahi_log_debug("generic"); */
+
+ if (rdlength > 0) {
+
+ r->data.generic.data = avahi_memdup(avahi_dns_packet_get_rptr(p), rdlength);
+
+ if (avahi_dns_packet_skip(p, rdlength) < 0)
+ return -1;
+ }
+
+ break;
+ }
+
+ /* Check if we read enough data */
+ if ((const uint8_t*) avahi_dns_packet_get_rptr(p) - (const uint8_t*) start != rdlength)
+ return -1;
+
+ return 0;
+}
+
+AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush) {
+ char name[AVAHI_DOMAIN_NAME_MAX];
+ uint16_t type, class;
+ uint32_t ttl;
+ uint16_t rdlength;
+ AvahiRecord *r = NULL;
+
+ assert(p);
+
+ 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 ||
+ avahi_dns_packet_consume_uint32(p, &ttl) < 0 ||
+ avahi_dns_packet_consume_uint16(p, &rdlength) < 0 ||
+ p->rindex + rdlength > p->size)
+ goto fail;
+
+ if (ret_cache_flush)
+ *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH);
+ class &= ~AVAHI_DNS_CACHE_FLUSH;
+
+ if (!(r = avahi_record_new_full(name, class, type, ttl)))
+ goto fail;
+
+ if (parse_rdata(p, r, rdlength) < 0)
+ goto fail;
+
+ if (!avahi_record_is_valid(r))
+ goto fail;
+
+ return r;
+
+fail:
+ if (r)
+ avahi_record_unref(r);
+
+ return NULL;
+}
+
+AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response) {
+ char name[256];
+ uint16_t type, class;
+ AvahiKey *k;
+
+ assert(p);
+
+ 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;
+
+ if (ret_unicast_response)
+ *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE);
+
+ class &= ~AVAHI_DNS_UNICAST_RESPONSE;
+
+ if (!(k = avahi_key_new(name, class, type)))
+ return NULL;
+
+ if (!avahi_key_is_valid(k)) {
+ avahi_key_unref(k);
+ return NULL;
+ }
+
+ return k;
+}
+
+uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response) {
+ uint8_t *t;
+ size_t size;
+
+ assert(p);
+ assert(k);
+
+ size = p->size;
+
+ if (!(t = avahi_dns_packet_append_name(p, k->name)) ||
+ !avahi_dns_packet_append_uint16(p, k->type) ||
+ !avahi_dns_packet_append_uint16(p, k->clazz | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) {
+ p->size = size;
+ return NULL;
+ }
+
+ return t;
+}
+
+static int append_rdata(AvahiDnsPacket *p, AvahiRecord *r) {
+ assert(p);
+ assert(r);
+
+ switch (r->key->type) {
+
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ case AVAHI_DNS_TYPE_NS:
+
+ if (!(avahi_dns_packet_append_name(p, r->data.ptr.name)))
+ return -1;
+
+ break;
+
+ case AVAHI_DNS_TYPE_SRV:
+
+ if (!avahi_dns_packet_append_uint16(p, r->data.srv.priority) ||
+ !avahi_dns_packet_append_uint16(p, r->data.srv.weight) ||
+ !avahi_dns_packet_append_uint16(p, r->data.srv.port) ||
+ !avahi_dns_packet_append_name(p, r->data.srv.name))
+ return -1;
+
+ break;
+
+ case AVAHI_DNS_TYPE_HINFO:
+ if (!avahi_dns_packet_append_string(p, r->data.hinfo.cpu) ||
+ !avahi_dns_packet_append_string(p, r->data.hinfo.os))
+ return -1;
+
+ break;
+
+ case AVAHI_DNS_TYPE_TXT: {
+
+ uint8_t *data;
+ size_t n;
+
+ n = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0);
+
+ if (!(data = avahi_dns_packet_extend(p, n)))
+ return -1;
+
+ avahi_string_list_serialize(r->data.txt.string_list, data, n);
+ break;
+ }
+
+
+ case AVAHI_DNS_TYPE_A:
+
+ if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address)))
+ return -1;
+
+ break;
+
+ case AVAHI_DNS_TYPE_AAAA:
+
+ if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address)))
+ return -1;
+
+ break;
+
+ default:
+
+ if (r->data.generic.size)
+ if (avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size))
+ return -1;
+
+ break;
+ }
+
+ return 0;
+}
+
+
+uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl) {
+ uint8_t *t, *l, *start;
+ size_t size;
+
+ assert(p);
+ assert(r);
+
+ size = p->size;
+
+ if (!(t = avahi_dns_packet_append_name(p, r->key->name)) ||
+ !avahi_dns_packet_append_uint16(p, r->key->type) ||
+ !avahi_dns_packet_append_uint16(p, cache_flush ? (r->key->clazz | AVAHI_DNS_CACHE_FLUSH) : (r->key->clazz &~ AVAHI_DNS_CACHE_FLUSH)) ||
+ !avahi_dns_packet_append_uint32(p, (max_ttl && r->ttl > max_ttl) ? max_ttl : r->ttl) ||
+ !(l = avahi_dns_packet_append_uint16(p, 0)))
+ goto fail;
+
+ start = avahi_dns_packet_extend(p, 0);
+
+ if (append_rdata(p, r) < 0)
+ goto fail;
+
+ size = avahi_dns_packet_extend(p, 0) - start;
+ assert(size <= 0xFFFF);
+
+/* avahi_log_debug("appended %u", size); */
+
+ l[0] = (uint8_t) ((uint16_t) size >> 8);
+ l[1] = (uint8_t) ((uint16_t) size);
+
+ return t;
+
+
+fail:
+ p->size = size;
+ return NULL;
+}
+
+int avahi_dns_packet_is_empty(AvahiDnsPacket *p) {
+ assert(p);
+
+ return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE;
+}
+
+size_t avahi_dns_packet_space(AvahiDnsPacket *p) {
+ assert(p);
+
+ assert(p->size <= p->max_size);
+
+ return p->max_size - p->size;
+}
+
+int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size) {
+ int ret;
+ AvahiDnsPacket p;
+
+ assert(record);
+ assert(rdata);
+
+ p.data = (void*) rdata;
+ p.max_size = p.size = size;
+ p.rindex = 0;
+ p.name_table = NULL;
+
+ ret = parse_rdata(&p, record, size);
+
+ assert(!p.name_table);
+
+ return ret;
+}
+
+size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size) {
+ int ret;
+ AvahiDnsPacket p;
+
+ assert(record);
+ assert(rdata);
+ assert(max_size > 0);
+
+ p.data = (void*) rdata;
+ p.max_size = max_size;
+ p.size = p.rindex = 0;
+ p.name_table = NULL;
+
+ ret = append_rdata(&p, record);
+
+ if (p.name_table)
+ avahi_hashmap_free(p.name_table);
+
+ if (ret < 0)
+ return (size_t) -1;
+
+ return p.size;
+}
diff --git a/trunk/avahi-core/dns.h b/trunk/avahi-core/dns.h
new file mode 100644
index 0000000..d1c06a5
--- /dev/null
+++ b/trunk/avahi-core/dns.h
@@ -0,0 +1,112 @@
+#ifndef foodnshfoo
+#define foodnshfoo
+
+/* $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 "rr.h"
+#include "hashmap.h"
+
+#define AVAHI_DNS_PACKET_SIZE_MAX 9000
+#define AVAHI_DNS_PACKET_HEADER_SIZE 12
+#define AVAHI_DNS_PACKET_EXTRA_SIZE 48
+#define AVAHI_DNS_LABELS_MAX 127
+
+typedef struct AvahiDnsPacket {
+ size_t size, rindex, max_size;
+ AvahiHashmap *name_table; /* for name compression */
+ uint8_t *data;
+} AvahiDnsPacket;
+
+#define AVAHI_DNS_PACKET_DATA(p) ((p)->data ? (p)->data : ((uint8_t*) p) + sizeof(AvahiDnsPacket))
+
+AvahiDnsPacket* avahi_dns_packet_new(unsigned mtu);
+AvahiDnsPacket* avahi_dns_packet_new_query(unsigned mtu);
+AvahiDnsPacket* avahi_dns_packet_new_response(unsigned mtu, int aa);
+
+AvahiDnsPacket* avahi_dns_packet_new_reply(AvahiDnsPacket* p, unsigned mtu, int copy_queries, int aa);
+
+void avahi_dns_packet_free(AvahiDnsPacket *p);
+void avahi_dns_packet_set_field(AvahiDnsPacket *p, unsigned idx, uint16_t v);
+uint16_t avahi_dns_packet_get_field(AvahiDnsPacket *p, unsigned idx);
+void avahi_dns_packet_inc_field(AvahiDnsPacket *p, unsigned idx);
+
+uint8_t *avahi_dns_packet_extend(AvahiDnsPacket *p, size_t l);
+
+uint8_t *avahi_dns_packet_append_uint16(AvahiDnsPacket *p, uint16_t v);
+uint8_t *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, uint32_t v);
+uint8_t *avahi_dns_packet_append_name(AvahiDnsPacket *p, const char *name);
+uint8_t *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, const void *d, size_t l);
+uint8_t* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, int unicast_response);
+uint8_t* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, int cache_flush, unsigned max_ttl);
+uint8_t* avahi_dns_packet_append_string(AvahiDnsPacket *p, const char *s);
+
+int avahi_dns_packet_is_query(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);
+int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l);
+int avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, void* ret_data, size_t l);
+AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, int *ret_unicast_response);
+AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, int *ret_cache_flush);
+int avahi_dns_packet_consume_string(AvahiDnsPacket *p, char *ret_string, size_t l);
+
+const void* avahi_dns_packet_get_rptr(AvahiDnsPacket *p);
+
+int avahi_dns_packet_skip(AvahiDnsPacket *p, size_t length);
+
+int avahi_dns_packet_is_empty(AvahiDnsPacket *p);
+size_t avahi_dns_packet_space(AvahiDnsPacket *p);
+
+#define AVAHI_DNS_FIELD_ID 0
+#define AVAHI_DNS_FIELD_FLAGS 1
+#define AVAHI_DNS_FIELD_QDCOUNT 2
+#define AVAHI_DNS_FIELD_ANCOUNT 3
+#define AVAHI_DNS_FIELD_NSCOUNT 4
+#define AVAHI_DNS_FIELD_ARCOUNT 5
+
+#define AVAHI_DNS_FLAG_QR (1 << 15)
+#define AVAHI_DNS_FLAG_OPCODE (15 << 11)
+#define AVAHI_DNS_FLAG_RCODE (15)
+#define AVAHI_DNS_FLAG_TC (1 << 9)
+#define AVAHI_DNS_FLAG_AA (1 << 10)
+
+#define AVAHI_DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \
+ (((uint16_t) !!qr << 15) | \
+ ((uint16_t) (opcode & 15) << 11) | \
+ ((uint16_t) !!aa << 10) | \
+ ((uint16_t) !!tc << 9) | \
+ ((uint16_t) !!rd << 8) | \
+ ((uint16_t) !!ra << 7) | \
+ ((uint16_t) !!ad << 5) | \
+ ((uint16_t) !!cd << 4) | \
+ ((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"
+
+#define AVAHI_DNS_RDATA_MAX 65535
+
+#endif
+
diff --git a/trunk/avahi-core/domain-util.c b/trunk/avahi-core/domain-util.c
new file mode 100644
index 0000000..3aeba12
--- /dev/null
+++ b/trunk/avahi-core/domain-util.c
@@ -0,0 +1,135 @@
+/* $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 <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <sys/utsname.h>
+#include <stdio.h>
+
+#include <avahi-common/malloc.h>
+
+#include "domain-util.h"
+#include "util.h"
+
+static void strip_bad_chars(char *s) {
+ char *p, *d;
+
+ s[strcspn(s, ".")] = 0;
+
+ for (p = s, d = s; *p; p++)
+ if ((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9') ||
+ *p == '-')
+ *(d++) = *p;
+
+ *d = 0;
+}
+
+char *avahi_get_host_name(char *ret_s, size_t size) {
+ assert(ret_s);
+ assert(size > 0);
+
+ if (gethostname(ret_s, size) >= 0) {
+ ret_s[size-1] = 0;
+ strip_bad_chars(ret_s);
+ } else
+ *ret_s = 0;
+
+ if (*ret_s == 0) {
+ struct utsname utsname;
+
+ /* No hostname was set, so let's take the OS name */
+
+ if (uname(&utsname) >= 0) {
+ snprintf(ret_s, size, "%s", utsname.sysname);
+ strip_bad_chars(ret_s);
+ avahi_strdown(ret_s);
+ }
+
+ if (*ret_s == 0)
+ snprintf(ret_s, size, "unnamed");
+ }
+
+ if (size >= AVAHI_LABEL_MAX)
+ ret_s[AVAHI_LABEL_MAX-1] = 0;
+
+ return ret_s;
+}
+
+char *avahi_get_host_name_strdup(void) {
+ char t[AVAHI_DOMAIN_NAME_MAX];
+
+ if (!(avahi_get_host_name(t, sizeof(t))))
+ return NULL;
+
+ return avahi_strdup(t);
+}
+
+int avahi_binary_domain_cmp(const char *a, const char *b) {
+ assert(a);
+ assert(b);
+
+ if (a == b)
+ return 0;
+
+ for (;;) {
+ char ca[AVAHI_LABEL_MAX], cb[AVAHI_LABEL_MAX], *p;
+ int r;
+
+ p = avahi_unescape_label(&a, ca, sizeof(ca));
+ assert(p);
+ p = avahi_unescape_label(&b, cb, sizeof(cb));
+ assert(p);
+
+ if ((r = strcmp(ca, cb)))
+ return r;
+
+ if (!*a && !*b)
+ return 0;
+ }
+}
+
+int avahi_domain_ends_with(const char *domain, const char *suffix) {
+ assert(domain);
+ assert(suffix);
+
+ for (;;) {
+ char dummy[AVAHI_LABEL_MAX], *r;
+
+ if (*domain == 0)
+ return 0;
+
+ if (avahi_domain_equal(domain, suffix))
+ return 1;
+
+ r = avahi_unescape_label(&domain, dummy, sizeof(dummy));
+ assert(r);
+ }
+}
+
diff --git a/trunk/avahi-core/domain-util.h b/trunk/avahi-core/domain-util.h
new file mode 100644
index 0000000..01233d8
--- /dev/null
+++ b/trunk/avahi-core/domain-util.h
@@ -0,0 +1,47 @@
+#ifndef foodomainutilhfoo
+#define foodomainutilhfoo
+
+/* $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 <inttypes.h>
+#include <sys/types.h>
+
+#include <avahi-common/cdecl.h>
+#include <avahi-common/domain.h>
+
+AVAHI_C_DECL_BEGIN
+
+/** Return the local host name. */
+char *avahi_get_host_name(char *ret_s, size_t size);
+
+/** Return the local host name. avahi_free() the result! */
+char *avahi_get_host_name_strdup(void);
+
+/** Do a binary comparison of to specified domain names, return -1, 0, or 1, depending on the order. */
+int avahi_binary_domain_cmp(const char *a, const char *b);
+
+/** Returns 1 if the the end labels of domain are eqal to suffix */
+int avahi_domain_ends_with(const char *domain, const char *suffix);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/entry.c b/trunk/avahi-core/entry.c
new file mode 100644
index 0000000..9e9c0dd
--- /dev/null
+++ b/trunk/avahi-core/entry.c
@@ -0,0 +1,1206 @@
+/* $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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include <arpa/inet.h>
+
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+
+#include "internal.h"
+#include "iface.h"
+#include "socket.h"
+#include "browse.h"
+#include "log.h"
+#include "util.h"
+#include "dns-srv-rr.h"
+#include "rr-util.h"
+#include "domain-util.h"
+
+static void transport_flags_from_domain(AvahiServer *s, AvahiPublishFlags *flags, const char *domain) {
+ assert(flags);
+ assert(domain);
+
+ assert(!((*flags & AVAHI_PUBLISH_USE_MULTICAST) && (*flags & AVAHI_PUBLISH_USE_WIDE_AREA)));
+
+ if (*flags & (AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_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_PUBLISH_USE_MULTICAST;
+ else
+ *flags |= AVAHI_PUBLISH_USE_WIDE_AREA;
+}
+
+void avahi_entry_free(AvahiServer*s, AvahiEntry *e) {
+ AvahiEntry *t;
+
+ assert(s);
+ assert(e);
+
+ avahi_goodbye_entry(s, e, 1, 1);
+
+ /* Remove from linked list */
+ AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
+
+ /* Remove from hash table indexed by name */
+ t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
+ AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
+ if (t)
+ avahi_hashmap_replace(s->entries_by_key, t->record->key, t);
+ else
+ avahi_hashmap_remove(s->entries_by_key, e->record->key);
+
+ /* Remove from associated group */
+ if (e->group)
+ AVAHI_LLIST_REMOVE(AvahiEntry, by_group, e->group->entries, e);
+
+ avahi_record_unref(e->record);
+ avahi_free(e);
+}
+
+void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g) {
+ assert(s);
+ assert(g);
+
+ while (g->entries)
+ avahi_entry_free(s, g->entries);
+
+ if (g->register_time_event)
+ avahi_time_event_free(g->register_time_event);
+
+ AVAHI_LLIST_REMOVE(AvahiSEntryGroup, groups, s->groups, g);
+ avahi_free(g);
+}
+
+void avahi_cleanup_dead_entries(AvahiServer *s) {
+ assert(s);
+
+ if (s->need_group_cleanup) {
+ AvahiSEntryGroup *g, *next;
+
+ for (g = s->groups; g; g = next) {
+ next = g->groups_next;
+
+ if (g->dead)
+ avahi_entry_group_free(s, g);
+ }
+
+ s->need_group_cleanup = 0;
+ }
+
+ if (s->need_entry_cleanup) {
+ AvahiEntry *e, *next;
+
+ for (e = s->entries; e; e = next) {
+ next = e->entries_next;
+
+ if (e->dead)
+ avahi_entry_free(s, e);
+ }
+
+ s->need_entry_cleanup = 0;
+ }
+
+ if (s->need_browser_cleanup)
+ avahi_browser_cleanup(s);
+}
+
+static int check_record_conflict(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *r, AvahiPublishFlags flags) {
+ AvahiEntry *e;
+
+ assert(s);
+ assert(r);
+
+ for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
+ if (e->dead)
+ continue;
+
+ if (!(flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_UNIQUE))
+ continue;
+
+ if ((flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) && (e->flags & AVAHI_PUBLISH_ALLOW_MULTIPLE) )
+ continue;
+
+ if ((interface <= 0 ||
+ e->interface <= 0 ||
+ e->interface == interface) &&
+ (protocol == AVAHI_PROTO_UNSPEC ||
+ e->protocol == AVAHI_PROTO_UNSPEC ||
+ e->protocol == protocol))
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static AvahiEntry * server_add_internal(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ AvahiRecord *r) {
+
+ AvahiEntry *e;
+
+ assert(s);
+ assert(r);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, s->state != AVAHI_SERVER_FAILURE && s->state != AVAHI_SERVER_INVALID, AVAHI_ERR_BAD_STATE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(
+ flags,
+ AVAHI_PUBLISH_NO_ANNOUNCE|
+ AVAHI_PUBLISH_NO_PROBE|
+ AVAHI_PUBLISH_UNIQUE|
+ AVAHI_PUBLISH_ALLOW_MULTIPLE|
+ AVAHI_PUBLISH_UPDATE|
+ AVAHI_PUBLISH_USE_WIDE_AREA|
+ AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
+ (r->key->type != 0) &&
+ (r->key->type != AVAHI_DNS_TYPE_ANY) &&
+ (r->key->type != AVAHI_DNS_TYPE_OPT) &&
+ (r->key->type != AVAHI_DNS_TYPE_TKEY) &&
+ (r->key->type != AVAHI_DNS_TYPE_TSIG) &&
+ (r->key->type != AVAHI_DNS_TYPE_IXFR) &&
+ (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE);
+
+ transport_flags_from_domain(s, &flags, r->key->name);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, AVAHI_ERR_NOT_PERMITTED);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s,
+ !g ||
+ (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) ||
+ (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE);
+
+ if (flags & AVAHI_PUBLISH_UPDATE) {
+ AvahiRecord *old_record;
+ int is_first = 1;
+
+ /* Update and existing record */
+
+ /* Find the first matching entry */
+ for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
+ if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol)
+ break;
+
+ is_first = 0;
+ }
+
+ /* Hmm, nothing found? */
+ if (!e) {
+ avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
+ return NULL;
+ }
+
+ /* Update the entry */
+ old_record = e->record;
+ e->record = avahi_record_ref(r);
+ e->flags = flags;
+
+ /* Announce our changes when needed */
+ if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) {
+
+ /* Remove the old entry from all caches, if needed */
+ if (!(e->flags & AVAHI_PUBLISH_UNIQUE))
+ avahi_goodbye_entry(s, e, 1, 0);
+
+ /* Reannounce our updated entry */
+ avahi_reannounce_entry(s, e);
+ }
+
+ /* If we were the first entry in the list, we need to update the key */
+ if (is_first)
+ avahi_hashmap_replace(s->entries_by_key, e->record->key, e);
+
+ avahi_record_unref(old_record);
+
+ } else {
+ AvahiEntry *t;
+
+ /* Add a new record */
+
+ if (check_record_conflict(s, interface, protocol, r, flags) < 0) {
+ avahi_server_set_errno(s, AVAHI_ERR_COLLISION);
+ return NULL;
+ }
+
+ if (!(e = avahi_new(AvahiEntry, 1))) {
+ avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ e->server = s;
+ e->record = avahi_record_ref(r);
+ e->group = g;
+ e->interface = interface;
+ e->protocol = protocol;
+ e->flags = flags;
+ e->dead = 0;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers);
+
+ AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
+
+ /* Insert into hash table indexed by name */
+ t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
+ AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
+ avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
+
+ /* Insert into group list */
+ if (g)
+ AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e);
+
+ avahi_announce_entry(s, e);
+ }
+
+ return e;
+}
+
+int avahi_server_add(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ AvahiRecord *r) {
+
+ if (!server_add_internal(s, g, interface, protocol, flags, r))
+ return avahi_server_errno(s);
+
+ return AVAHI_OK;
+}
+
+const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
+ AvahiEntry **e = (AvahiEntry**) state;
+ assert(s);
+ assert(e);
+
+ if (!*e)
+ *e = g ? g->entries : s->entries;
+
+ while (*e && (*e)->dead)
+ *e = g ? (*e)->by_group_next : (*e)->entries_next;
+
+ if (!*e)
+ return NULL;
+
+ return avahi_record_ref((*e)->record);
+}
+
+int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
+ AvahiEntry *e;
+
+ assert(s);
+ assert(callback);
+
+ callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
+
+ for (e = s->entries; e; e = e->entries_next) {
+ char *t;
+ char ln[256];
+
+ if (e->dead)
+ continue;
+
+ if (!(t = avahi_record_to_string(e->record)))
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+
+ snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
+ avahi_free(t);
+
+ callback(ln, 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;
+}
+
+static AvahiEntry *server_add_ptr_internal(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ uint32_t ttl,
+ const char *name,
+ const char *dest) {
+
+ AvahiRecord *r;
+ AvahiEntry *e;
+
+ assert(s);
+ assert(dest);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), AVAHI_ERR_INVALID_HOST_NAME);
+
+ if (!name)
+ name = s->host_name_fqdn;
+
+ if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl))) {
+ avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ r->data.ptr.name = avahi_normalize_name_strdup(dest);
+ e = server_add_internal(s, g, interface, protocol, flags, r);
+ avahi_record_unref(r);
+ return e;
+}
+
+int avahi_server_add_ptr(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ uint32_t ttl,
+ const char *name,
+ const char *dest) {
+
+ AvahiEntry *e;
+
+ assert(s);
+
+ if (!(e = server_add_ptr_internal(s, g, interface, protocol, flags, ttl, name, dest)))
+ return avahi_server_errno(s);
+
+ return AVAHI_OK;
+}
+
+int avahi_server_add_address(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ AvahiAddress *a) {
+
+ char n[AVAHI_DOMAIN_NAME_MAX];
+ int ret = AVAHI_OK;
+ AvahiEntry *entry = NULL, *reverse = NULL;
+ AvahiRecord *r;
+
+ assert(s);
+ assert(a);
+
+ AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags,
+ AVAHI_PUBLISH_NO_REVERSE|
+ AVAHI_PUBLISH_NO_ANNOUNCE|
+ AVAHI_PUBLISH_NO_PROBE|
+ AVAHI_PUBLISH_UPDATE|
+ AVAHI_PUBLISH_USE_WIDE_AREA|
+ AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME);
+
+ /* Prepare the host naem */
+
+ if (!name)
+ name = s->host_name_fqdn;
+ else {
+ AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n)));
+ name = n;
+ }
+
+ transport_flags_from_domain(s, &flags, name);
+ AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
+
+ /* Create the A/AAAA record */
+
+ if (a->proto == AVAHI_PROTO_INET) {
+
+ if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto finish;
+ }
+
+ r->data.a.address = a->data.ipv4;
+
+ } else {
+ assert(a->proto == AVAHI_PROTO_INET6);
+
+ if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto finish;
+ }
+
+ r->data.aaaa.address = a->data.ipv6;
+ }
+
+ entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
+ avahi_record_unref(r);
+
+ if (!entry) {
+ ret = avahi_server_errno(s);
+ goto finish;
+ }
+
+ /* Create the reverse lookup entry */
+
+ if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
+ char reverse_n[AVAHI_DOMAIN_NAME_MAX];
+ avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n));
+
+ if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse_n, name))) {
+ ret = avahi_server_errno(s);
+ goto finish;
+ }
+ }
+
+finish:
+
+ if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
+ if (entry)
+ avahi_entry_free(s, entry);
+ if (reverse)
+ avahi_entry_free(s, reverse);
+ }
+
+ return ret;
+}
+
+static AvahiEntry *server_add_txt_strlst_nocopy(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ uint32_t ttl,
+ const char *name,
+ AvahiStringList *strlst) {
+
+ AvahiRecord *r;
+ AvahiEntry *e;
+
+ assert(s);
+
+ if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) {
+ avahi_string_list_free(strlst);
+ avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ r->data.txt.string_list = strlst;
+ e = server_add_internal(s, g, interface, protocol, flags, r);
+ avahi_record_unref(r);
+
+ return e;
+}
+
+static AvahiStringList *add_magic_cookie(
+ AvahiServer *s,
+ AvahiStringList *strlst) {
+
+ assert(s);
+
+ if (!s->config.add_service_cookie)
+ return strlst;
+
+ if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE))
+ /* This string list already contains a magic cookie */
+ return strlst;
+
+ return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie);
+}
+
+static int server_add_service_strlst_nocopy(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ AvahiStringList *strlst) {
+
+ char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL;
+ AvahiRecord *r = NULL;
+ int ret = AVAHI_OK;
+ AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL;
+
+ assert(s);
+ assert(type);
+ assert(name);
+
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags,
+ AVAHI_PUBLISH_NO_COOKIE|
+ AVAHI_PUBLISH_UPDATE|
+ AVAHI_PUBLISH_USE_WIDE_AREA|
+ AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_fqdn(host), AVAHI_ERR_INVALID_HOST_NAME);
+
+ if (!domain)
+ domain = s->domain_name;
+
+ if (!host)
+ host = s->host_name_fqdn;
+
+ transport_flags_from_domain(s, &flags, domain);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
+
+ if (!(h = avahi_normalize_name_strdup(host))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
+ (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
+ (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
+ avahi_server_set_errno(s, ret);
+ goto fail;
+ }
+
+ /* Add service enumeration PTR record */
+
+ if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) {
+ ret = avahi_server_errno(s);
+ goto fail;
+ }
+
+ /* Add SRV record */
+
+ if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
+ ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ goto fail;
+ }
+
+ r->data.srv.priority = 0;
+ r->data.srv.weight = 0;
+ r->data.srv.port = port;
+ r->data.srv.name = h;
+ h = NULL;
+ srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r);
+ avahi_record_unref(r);
+
+ if (!srv_entry) {
+ ret = avahi_server_errno(s);
+ goto fail;
+ }
+
+ /* Add TXT record */
+
+ if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
+ strlst = add_magic_cookie(s, strlst);
+
+ txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
+ strlst = NULL;
+
+ if (!txt_entry) {
+ ret = avahi_server_errno(s);
+ goto fail;
+ }
+
+ /* Add service type enumeration record */
+
+ if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) {
+ ret = avahi_server_errno(s);
+ goto fail;
+ }
+
+fail:
+ if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
+ if (srv_entry)
+ avahi_entry_free(s, srv_entry);
+ if (txt_entry)
+ avahi_entry_free(s, txt_entry);
+ if (ptr_entry)
+ avahi_entry_free(s, ptr_entry);
+ if (enum_entry)
+ avahi_entry_free(s, enum_entry);
+ }
+
+ avahi_string_list_free(strlst);
+ avahi_free(h);
+
+ return ret;
+}
+
+int avahi_server_add_service_strlst(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ AvahiStringList *strlst) {
+
+ assert(s);
+ assert(type);
+ assert(name);
+
+ return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst));
+}
+
+int avahi_server_add_service(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ ... ){
+
+ va_list va;
+ int ret;
+
+ va_start(va, port);
+ ret = server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va));
+ va_end(va);
+
+ return ret;
+}
+
+static int server_update_service_txt_strlst_nocopy(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AvahiStringList *strlst) {
+
+ char svc_name[AVAHI_DOMAIN_NAME_MAX];
+ int ret = AVAHI_OK;
+ AvahiEntry *e;
+
+ assert(s);
+ assert(type);
+ assert(name);
+
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags,
+ AVAHI_PUBLISH_NO_COOKIE|
+ AVAHI_PUBLISH_USE_WIDE_AREA|
+ AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+
+ if (!domain)
+ domain = s->domain_name;
+
+ transport_flags_from_domain(s, &flags, domain);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
+
+ if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) {
+ avahi_server_set_errno(s, ret);
+ goto fail;
+ }
+
+ /* Add TXT record */
+ if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
+ strlst = add_magic_cookie(s, strlst);
+
+ e = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst);
+ strlst = NULL;
+
+ if (!e)
+ ret = avahi_server_errno(s);
+
+fail:
+
+ avahi_string_list_free(strlst);
+
+ return ret;
+}
+
+int avahi_server_update_service_txt_strlst(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AvahiStringList *strlst) {
+
+ return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst));
+}
+
+/** Update the TXT record for a service with the NULL termonate list of strings */
+int avahi_server_update_service_txt(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ ...) {
+
+ va_list va;
+ int ret;
+
+ va_start(va, domain);
+ ret = server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va));
+ va_end(va);
+
+ return ret;
+}
+
+int avahi_server_add_service_subtype(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *subtype) {
+
+ int ret = AVAHI_OK;
+ char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX];
+
+ assert(name);
+ assert(type);
+ assert(subtype);
+
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE);
+
+ if (!domain)
+ domain = s->domain_name;
+
+ transport_flags_from_domain(s, &flags, domain);
+ AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
+
+ if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
+ (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) {
+ avahi_server_set_errno(s, ret);
+ goto fail;
+ }
+
+ if ((ret = avahi_server_add_ptr(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
+ goto fail;
+
+fail:
+
+ return ret;
+}
+
+static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
+ static const char hex[] = "0123456789abcdef";
+ int b = 0;
+ const uint8_t *k = p;
+
+ while (sl > 1 && pl > 0) {
+ *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
+
+ if (b) {
+ k++;
+ pl--;
+ }
+
+ b = !b;
+
+ sl--;
+ }
+
+ if (sl > 0)
+ *s = 0;
+}
+
+static AvahiEntry *server_add_dns_server_name(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *domain,
+ AvahiDNSServerType type,
+ const char *name,
+ uint16_t port /** should be 53 */) {
+
+ AvahiEntry *e;
+ char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n;
+
+ AvahiRecord *r;
+
+ assert(s);
+ assert(name);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_WIDE_AREA|AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_fqdn(name), AVAHI_ERR_INVALID_HOST_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+
+ if (!domain)
+ domain = s->domain_name;
+
+ transport_flags_from_domain(s, &flags, domain);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
+
+ if (!(n = avahi_normalize_name_strdup(name))) {
+ avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d)));
+
+ snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d);
+
+ if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
+ avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ avahi_free(n);
+ return NULL;
+ }
+
+ r->data.srv.priority = 0;
+ r->data.srv.weight = 0;
+ r->data.srv.port = port;
+ r->data.srv.name = n;
+ e = server_add_internal(s, g, interface, protocol, 0, r);
+ avahi_record_unref(r);
+
+ return e;
+}
+
+int avahi_server_add_dns_server_address(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *domain,
+ AvahiDNSServerType type,
+ const AvahiAddress *address,
+ uint16_t port /** should be 53 */) {
+
+ AvahiRecord *r;
+ char n[64], h[64];
+ AvahiEntry *a_entry, *s_entry;
+
+ assert(s);
+ assert(address);
+
+ AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
+ AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT);
+ AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+
+ if (!domain)
+ domain = s->domain_name;
+
+ transport_flags_from_domain(s, &flags, domain);
+ AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
+
+ if (address->proto == AVAHI_PROTO_INET) {
+ hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
+ snprintf(n, sizeof(n), "ip-%s.%s", h, domain);
+ 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(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address));
+ snprintf(n, sizeof(n), "ip6-%s.%s", h, domain);
+ 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;
+ }
+
+ if (!r)
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+
+ a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
+ avahi_record_unref(r);
+
+ if (!a_entry)
+ return avahi_server_errno(s);
+
+ if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) {
+ if (!(flags & AVAHI_PUBLISH_UPDATE))
+ avahi_entry_free(s, a_entry);
+ return avahi_server_errno(s);
+ }
+
+ return AVAHI_OK;
+}
+
+void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
+ assert(g);
+
+ if (g->state == state)
+ return;
+
+ assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
+
+ if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
+
+ /* If the entry group was established for a time longer then
+ * 5s, reset the establishment trial counter */
+
+ if (avahi_age(&g->established_at) > 5000000)
+ g->n_register_try = 0;
+ }
+
+ if (state == AVAHI_ENTRY_GROUP_ESTABLISHED)
+
+ /* If the entry group is now established, remember the time
+ * this happened */
+
+ gettimeofday(&g->established_at, NULL);
+
+ g->state = state;
+
+ if (g->callback)
+ g->callback(g->server, g, state, g->userdata);
+}
+
+AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
+ AvahiSEntryGroup *g;
+
+ assert(s);
+
+ if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
+ avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ g->server = s;
+ g->callback = callback;
+ g->userdata = userdata;
+ g->dead = 0;
+ g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
+ g->n_probing = 0;
+ g->n_register_try = 0;
+ g->register_time_event = NULL;
+ g->register_time.tv_sec = 0;
+ g->register_time.tv_usec = 0;
+ AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
+
+ AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
+ return g;
+}
+
+void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
+ AvahiEntry *e;
+
+ assert(g);
+ assert(g->server);
+
+ for (e = g->entries; e; e = e->by_group_next) {
+ if (!e->dead) {
+ avahi_goodbye_entry(g->server, e, 1, 1);
+ e->dead = 1;
+ }
+ }
+
+ if (g->register_time_event) {
+ avahi_time_event_free(g->register_time_event);
+ g->register_time_event = NULL;
+ }
+
+ g->dead = 1;
+
+ g->server->need_group_cleanup = 1;
+ g->server->need_entry_cleanup = 1;
+}
+
+static void entry_group_commit_real(AvahiSEntryGroup *g) {
+ assert(g);
+
+ gettimeofday(&g->register_time, NULL);
+
+ avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
+
+ if (g->dead)
+ return;
+
+ avahi_announce_group(g->server, g);
+ avahi_s_entry_group_check_probed(g, 0);
+}
+
+static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) {
+ AvahiSEntryGroup *g = userdata;
+ assert(g);
+
+ avahi_time_event_free(g->register_time_event);
+ g->register_time_event = NULL;
+
+ /* Holdoff time passed, so let's start probing */
+ entry_group_commit_real(g);
+}
+
+int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
+ struct timeval now;
+
+ assert(g);
+ assert(!g->dead);
+
+ if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
+ return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
+
+ if (avahi_s_entry_group_is_empty(g))
+ return avahi_server_set_errno(g->server, AVAHI_ERR_IS_EMPTY);
+
+ g->n_register_try++;
+
+ avahi_timeval_add(&g->register_time,
+ 1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
+ AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
+ AVAHI_RR_HOLDOFF_MSEC));
+
+ gettimeofday(&now, NULL);
+
+ if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
+
+ /* Holdoff time passed, so let's start probing */
+ entry_group_commit_real(g);
+ } else {
+
+ /* Holdoff time has not yet passed, so let's wait */
+ assert(!g->register_time_event);
+ g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
+
+ avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
+ }
+
+ return AVAHI_OK;
+}
+
+void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
+ AvahiEntry *e;
+ assert(g);
+
+ for (e = g->entries; e; e = e->by_group_next) {
+ if (!e->dead) {
+ avahi_goodbye_entry(g->server, e, 1, 1);
+ e->dead = 1;
+ }
+ }
+ g->server->need_entry_cleanup = 1;
+
+ if (g->register_time_event) {
+ avahi_time_event_free(g->register_time_event);
+ g->register_time_event = NULL;
+ }
+
+ g->n_probing = 0;
+
+ gettimeofday(&g->register_time, NULL);
+
+ avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
+}
+
+int avahi_entry_is_commited(AvahiEntry *e) {
+ assert(e);
+ assert(!e->dead);
+
+ return !e->group ||
+ e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
+ e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
+}
+
+AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
+ assert(g);
+ assert(!g->dead);
+
+ return g->state;
+}
+
+void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
+ assert(g);
+
+ g->userdata = userdata;
+}
+
+void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
+ assert(g);
+
+ return g->userdata;
+}
+
+int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
+ AvahiEntry *e;
+ assert(g);
+
+ /* Look for an entry that is not dead */
+ for (e = g->entries; e; e = e->by_group_next)
+ if (!e->dead)
+ return 0;
+
+ return 1;
+}
diff --git a/trunk/avahi-core/fdutil.c b/trunk/avahi-core/fdutil.c
new file mode 100644
index 0000000..de7b0cf
--- /dev/null
+++ b/trunk/avahi-core/fdutil.c
@@ -0,0 +1,73 @@
+/* $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 <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "fdutil.h"
+
+int avahi_set_cloexec(int fd) {
+ int n;
+
+ assert(fd >= 0);
+
+ if ((n = fcntl(fd, F_GETFD)) < 0)
+ return -1;
+
+ if (n & FD_CLOEXEC)
+ return 0;
+
+ return fcntl(fd, F_SETFD, n|FD_CLOEXEC);
+}
+
+int avahi_set_nonblock(int fd) {
+ int n;
+
+ assert(fd >= 0);
+
+ if ((n = fcntl(fd, F_GETFL)) < 0)
+ return -1;
+
+ if (n & O_NONBLOCK)
+ return 0;
+
+ return fcntl(fd, F_SETFL, n|O_NONBLOCK);
+}
+
+int avahi_wait_for_write(int fd) {
+ fd_set fds;
+ int r;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0)
+ return -1;
+
+ assert(r > 0);
+
+ return 0;
+}
diff --git a/trunk/avahi-core/fdutil.h b/trunk/avahi-core/fdutil.h
new file mode 100644
index 0000000..047b9bb
--- /dev/null
+++ b/trunk/avahi-core/fdutil.h
@@ -0,0 +1,35 @@
+#ifndef foofdutilhfoo
+#define foofdutilhfoo
+
+/* $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 <avahi-common/cdecl.h>
+
+AVAHI_C_DECL_BEGIN
+
+int avahi_set_cloexec(int fd);
+int avahi_set_nonblock(int fd);
+int avahi_wait_for_write(int fd);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/findstatic.pl b/trunk/avahi-core/findstatic.pl
new file mode 100755
index 0000000..43a4916
--- /dev/null
+++ b/trunk/avahi-core/findstatic.pl
@@ -0,0 +1,70 @@
+#!/usr/bin/perl -w
+# find a list of fns and variables in the code that could be static
+# usually called with something like this:
+# findstatic.pl `find . -name "*.o"`
+# Andrew Tridgell <tridge@samba.org>
+
+use strict;
+
+# use nm to find the symbols
+my($saved_delim) = $/;
+undef $/;
+my($syms) = `nm -o @ARGV`;
+$/ = $saved_delim;
+
+my(@lines) = split(/\n/s, $syms);
+
+my(%def);
+my(%undef);
+my(%stype);
+
+my(%typemap) = (
+ "T" => "function",
+ "C" => "uninitialised variable",
+ "D" => "initialised variable"
+ );
+
+
+# parse the symbols into defined and undefined
+for (my($i)=0; $i <= $#{@lines}; $i++) {
+ my($line) = $lines[$i];
+ if ($line =~ /(.*):[a-f0-9]* ([TCD]) (.*)/) {
+ my($fname) = $1;
+ my($symbol) = $3;
+ push(@{$def{$fname}}, $symbol);
+ $stype{$symbol} = $2;
+ }
+ if ($line =~ /(.*):\s* U (.*)/) {
+ my($fname) = $1;
+ my($symbol) = $2;
+ push(@{$undef{$fname}}, $symbol);
+ }
+}
+
+# look for defined symbols that are never referenced outside the place they
+# are defined
+foreach my $f (keys %def) {
+ print "Checking $f\n";
+ my($found_one) = 0;
+ foreach my $s (@{$def{$f}}) {
+ my($found) = 0;
+ foreach my $f2 (keys %undef) {
+ if ($f2 ne $f) {
+ foreach my $s2 (@{$undef{$f2}}) {
+ if ($s2 eq $s) {
+ $found = 1;
+ $found_one = 1;
+ }
+ }
+ }
+ }
+ if ($found == 0) {
+ my($t) = $typemap{$stype{$s}};
+ print " '$s' is unique to $f ($t)\n";
+ }
+ }
+ if ($found_one == 0) {
+ print " all symbols in '$f' are unused (main program?)\n";
+ }
+}
+
diff --git a/trunk/avahi-core/hashmap-test.c b/trunk/avahi-core/hashmap-test.c
new file mode 100644
index 0000000..9826d53
--- /dev/null
+++ b/trunk/avahi-core/hashmap-test.c
@@ -0,0 +1,64 @@
+/* $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 <stdio.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include "hashmap.h"
+#include "util.h"
+
+int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) {
+ unsigned n;
+ AvahiHashmap *m;
+ const char *t;
+
+ m = avahi_hashmap_new(avahi_string_hash, avahi_string_equal, avahi_free, avahi_free);
+
+ avahi_hashmap_insert(m, avahi_strdup("bla"), avahi_strdup("#1"));
+ avahi_hashmap_insert(m, avahi_strdup("bla2"), avahi_strdup("asdf"));
+ avahi_hashmap_insert(m, avahi_strdup("gurke"), avahi_strdup("ffsdf"));
+ avahi_hashmap_insert(m, avahi_strdup("blubb"), avahi_strdup("sadfsd"));
+ avahi_hashmap_insert(m, avahi_strdup("bla"), avahi_strdup("#2"));
+
+ for (n = 0; n < 1000; n ++)
+ avahi_hashmap_insert(m, avahi_strdup_printf("key %u", n), avahi_strdup_printf("value %u", n));
+
+ printf("%s\n", (const char*) avahi_hashmap_lookup(m, "bla"));
+
+ avahi_hashmap_replace(m, avahi_strdup("bla"), avahi_strdup("#3"));
+
+ printf("%s\n", (const char*) avahi_hashmap_lookup(m, "bla"));
+
+ avahi_hashmap_remove(m, "bla");
+
+ t = (const char*) avahi_hashmap_lookup(m, "bla");
+ printf("%s\n", t ? t : "(null)");
+
+ avahi_hashmap_free(m);
+
+ return 0;
+}
diff --git a/trunk/avahi-core/hashmap.c b/trunk/avahi-core/hashmap.c
new file mode 100644
index 0000000..07bd707
--- /dev/null
+++ b/trunk/avahi-core/hashmap.c
@@ -0,0 +1,250 @@
+/* $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 <string.h>
+
+#include <avahi-common/llist.h>
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+
+#include "hashmap.h"
+#include "util.h"
+
+#define HASH_MAP_SIZE 123
+
+typedef struct Entry Entry;
+struct Entry {
+ AvahiHashmap *hashmap;
+ void *key;
+ void *value;
+
+ AVAHI_LLIST_FIELDS(Entry, bucket);
+ AVAHI_LLIST_FIELDS(Entry, entries);
+};
+
+struct AvahiHashmap {
+ AvahiHashFunc hash_func;
+ AvahiEqualFunc equal_func;
+ AvahiFreeFunc key_free_func, value_free_func;
+
+ Entry *entries[HASH_MAP_SIZE];
+ AVAHI_LLIST_HEAD(Entry, entries_list);
+};
+
+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)
+ if (m->equal_func(key, e->key))
+ return e;
+
+ return NULL;
+}
+
+static void entry_free(AvahiHashmap *m, Entry *e, int stolen) {
+ unsigned idx;
+ assert(m);
+ assert(e);
+
+ idx = m->hash_func(e->key) % HASH_MAP_SIZE;
+
+ AVAHI_LLIST_REMOVE(Entry, bucket, m->entries[idx], e);
+ AVAHI_LLIST_REMOVE(Entry, entries, m->entries_list, e);
+
+ if (m->key_free_func)
+ m->key_free_func(e->key);
+ if (m->value_free_func && !stolen)
+ m->value_free_func(e->value);
+
+ avahi_free(e);
+}
+
+AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func) {
+ AvahiHashmap *m;
+
+ assert(hash_func);
+ assert(equal_func);
+
+ if (!(m = avahi_new0(AvahiHashmap, 1)))
+ return NULL;
+
+ m->hash_func = hash_func;
+ m->equal_func = equal_func;
+ m->key_free_func = key_free_func;
+ m->value_free_func = value_free_func;
+
+ AVAHI_LLIST_HEAD_INIT(Entry, m->entries_list);
+
+ return m;
+}
+
+void avahi_hashmap_free(AvahiHashmap *m) {
+ assert(m);
+
+ while (m->entries_list)
+ entry_free(m, m->entries_list, 0);
+
+ avahi_free(m);
+}
+
+void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key) {
+ Entry *e;
+
+ assert(m);
+
+ if (!(e = entry_get(m, key)))
+ return NULL;
+
+ return e->value;
+}
+
+int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value) {
+ unsigned idx;
+ Entry *e;
+
+ assert(m);
+
+ if ((e = entry_get(m, key))) {
+ if (m->key_free_func)
+ m->key_free_func(key);
+ if (m->value_free_func)
+ m->value_free_func(value);
+
+ return 1;
+ }
+
+ if (!(e = avahi_new(Entry, 1)))
+ return -1;
+
+ e->hashmap = m;
+ e->key = key;
+ e->value = value;
+
+ AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e);
+
+ idx = m->hash_func(key) % HASH_MAP_SIZE;
+ AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e);
+
+ return 0;
+}
+
+
+int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value) {
+ unsigned idx;
+ Entry *e;
+
+ assert(m);
+
+ if ((e = entry_get(m, key))) {
+ if (m->key_free_func)
+ m->key_free_func(e->key);
+ if (m->value_free_func)
+ m->value_free_func(e->value);
+
+ e->key = key;
+ e->value = value;
+
+ return 1;
+ }
+
+ if (!(e = avahi_new(Entry, 1)))
+ return -1;
+
+ e->hashmap = m;
+ e->key = key;
+ e->value = value;
+
+ AVAHI_LLIST_PREPEND(Entry, entries, m->entries_list, e);
+
+ idx = m->hash_func(key) % HASH_MAP_SIZE;
+ AVAHI_LLIST_PREPEND(Entry, bucket, m->entries[idx], e);
+
+ return 0;
+}
+
+void avahi_hashmap_remove(AvahiHashmap *m, const void *key) {
+ Entry *e;
+
+ assert(m);
+
+ if (!(e = entry_get(m, key)))
+ return;
+
+ entry_free(m, e, 0);
+}
+
+void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata) {
+ Entry *e, *next;
+ assert(m);
+ assert(callback);
+
+ for (e = m->entries_list; e; e = next) {
+ next = e->entries_next;
+
+ callback(e->key, e->value, userdata);
+ }
+}
+
+unsigned avahi_string_hash(const void *data) {
+ const char *p = data;
+ unsigned hash = 0;
+
+ assert(p);
+
+ for (; *p; p++)
+ hash = 31 * hash + *p;
+
+ return hash;
+}
+
+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/trunk/avahi-core/hashmap.h b/trunk/avahi-core/hashmap.h
new file mode 100644
index 0000000..120cf30
--- /dev/null
+++ b/trunk/avahi-core/hashmap.h
@@ -0,0 +1,55 @@
+#ifndef foohashmaphfoo
+#define foohashmaphfoo
+
+/* $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 <avahi-common/cdecl.h>
+
+AVAHI_C_DECL_BEGIN
+
+typedef struct AvahiHashmap AvahiHashmap;
+
+typedef unsigned (*AvahiHashFunc)(const void *data);
+typedef int (*AvahiEqualFunc)(const void *a, const void *b);
+typedef void (*AvahiFreeFunc)(void *p);
+
+AvahiHashmap* avahi_hashmap_new(AvahiHashFunc hash_func, AvahiEqualFunc equal_func, AvahiFreeFunc key_free_func, AvahiFreeFunc value_free_func);
+
+void avahi_hashmap_free(AvahiHashmap *m);
+void* avahi_hashmap_lookup(AvahiHashmap *m, const void *key);
+int avahi_hashmap_insert(AvahiHashmap *m, void *key, void *value);
+int avahi_hashmap_replace(AvahiHashmap *m, void *key, void *value);
+void avahi_hashmap_remove(AvahiHashmap *m, const void *key);
+
+typedef void (*AvahiHashmapForeachCallback)(void *key, void *value, void *userdata);
+
+void avahi_hashmap_foreach(AvahiHashmap *m, AvahiHashmapForeachCallback callback, void *userdata);
+
+unsigned avahi_string_hash(const void *data);
+int avahi_string_equal(const void *a, const void *b);
+
+unsigned avahi_int_hash(const void *data);
+int avahi_int_equal(const void *a, const void *b);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/iface-linux.c b/trunk/avahi-core/iface-linux.c
new file mode 100644
index 0000000..c8ed9e0
--- /dev/null
+++ b/trunk/avahi-core/iface-linux.c
@@ -0,0 +1,371 @@
+/* $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 <string.h>
+#include <net/if.h>
+#include <errno.h>
+#include <string.h>
+
+#include <avahi-common/malloc.h>
+
+#include "log.h"
+#include "iface.h"
+#include "iface-linux.h"
+
+#ifndef IFLA_RTA
+#include <linux/if_addr.h>
+#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+
+#ifndef IFA_RTA
+#include <linux/if_addr.h>
+#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+
+static int netlink_list_items(AvahiNetlink *nl, uint16_t type, unsigned *ret_seq) {
+ struct nlmsghdr *n;
+ struct rtgenmsg *gen;
+ uint8_t req[1024];
+
+ /* Issue a wild dump NETLINK request */
+
+ memset(&req, 0, sizeof(req));
+ n = (struct nlmsghdr*) req;
+ n->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+ n->nlmsg_type = type;
+ n->nlmsg_flags = NLM_F_ROOT|NLM_F_REQUEST;
+ n->nlmsg_pid = 0;
+
+ gen = NLMSG_DATA(n);
+ memset(gen, 0, sizeof(struct rtgenmsg));
+ gen->rtgen_family = AF_UNSPEC;
+
+ return avahi_netlink_send(nl, n, ret_seq);
+}
+
+static void netlink_callback(AvahiNetlink *nl, struct nlmsghdr *n, void* userdata) {
+ AvahiInterfaceMonitor *m = userdata;
+
+ /* This routine is called for every RTNETLINK response packet */
+
+ assert(m);
+ assert(n);
+ assert(m->osdep.netlink == nl);
+
+ if (n->nlmsg_type == RTM_NEWLINK) {
+
+ /* A new interface appeared or an existing one has been modified */
+
+ struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
+ AvahiHwInterface *hw;
+ struct rtattr *a = NULL;
+ size_t l;
+
+ /* A (superfluous?) sanity check */
+ if (ifinfomsg->ifi_family != AF_UNSPEC)
+ return;
+
+ /* Check whether there already is an AvahiHwInterface object
+ * for this link, so that we can update its data. Note that
+ * Netlink sends us an RTM_NEWLINK not only when a new
+ * interface appears, but when it changes, too */
+
+ if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
+
+ /* No object found, so let's create a new
+ * one. avahi_hw_interface_new() will call
+ * avahi_interface_new() internally twice for IPv4 and
+ * IPv6, so there is no need for us to do that
+ * ourselves */
+ if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
+ return; /* OOM */
+
+ /* Check whether the flags of this interface are OK for us */
+ hw->flags_ok =
+ (ifinfomsg->ifi_flags & IFF_UP) &&
+ (!m->server->config.use_iff_running || (ifinfomsg->ifi_flags & IFF_RUNNING)) &&
+ !(ifinfomsg->ifi_flags & IFF_LOOPBACK) &&
+ (ifinfomsg->ifi_flags & IFF_MULTICAST) &&
+ (m->server->config.allow_point_to_point || !(ifinfomsg->ifi_flags & IFF_POINTOPOINT));
+
+ /* Handle interface attributes */
+ l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
+ a = IFLA_RTA(ifinfomsg);
+
+ while (RTA_OK(a, l)) {
+ switch(a->rta_type) {
+ case IFLA_IFNAME:
+
+ /* Fill in interface name */
+ avahi_free(hw->name);
+ hw->name = avahi_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
+ break;
+
+ case IFLA_MTU:
+
+ /* Fill in MTU */
+ assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
+ hw->mtu = *((unsigned int*) RTA_DATA(a));
+ break;
+
+ case IFLA_ADDRESS:
+
+ /* Fill in hardware (MAC) address */
+ hw->mac_address_size = RTA_PAYLOAD(a);
+ if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX)
+ hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX;
+
+ memcpy(hw->mac_address, RTA_DATA(a), hw->mac_address_size);
+ break;
+
+ default:
+ ;
+ }
+
+ a = RTA_NEXT(a, l);
+ }
+
+ /* Check whether this interface is now "relevant" for us. If
+ * it is Avahi will start to announce its records on this
+ * interface and send out queries for subscribed records on
+ * it */
+ avahi_hw_interface_check_relevant(hw);
+
+ /* Update any associated RRs of this interface. (i.e. the
+ * _workstation._tcp record containing the MAC address) */
+ avahi_hw_interface_update_rrs(hw, 0);
+
+ } else if (n->nlmsg_type == RTM_DELLINK) {
+
+ /* An interface has been removed */
+
+ struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
+ AvahiHwInterface *hw;
+
+ /* A (superfluous?) sanity check */
+ if (ifinfomsg->ifi_family != AF_UNSPEC)
+ return;
+
+ /* Get a reference to our AvahiHwInterface object of this interface */
+ if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifinfomsg->ifi_index)))
+ return;
+
+ /* Free our object */
+ avahi_hw_interface_free(hw, 0);
+
+ } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
+
+ /* An address has been added, modified or removed */
+
+ struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
+ AvahiInterface *i;
+ struct rtattr *a = NULL;
+ size_t l;
+ AvahiAddress raddr;
+ int raddr_valid = 0;
+
+ /* We are only interested in IPv4 and IPv6 */
+ if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
+ return;
+
+ /* Try to get a reference to our AvahiInterface object for the
+ * interface this address is assigned to. If ther is no object
+ * for this interface, we ignore this address. */
+ if (!(i = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifaddrmsg->ifa_index, avahi_af_to_proto(ifaddrmsg->ifa_family))))
+ return;
+
+ /* Fill in address family for our new address */
+ raddr.proto = avahi_af_to_proto(ifaddrmsg->ifa_family);
+
+ l = NLMSG_PAYLOAD(n, sizeof(struct ifaddrmsg));
+ a = IFA_RTA(ifaddrmsg);
+
+ while (RTA_OK(a, l)) {
+
+ switch(a->rta_type) {
+ case IFA_ADDRESS:
+ /* Fill in address data */
+
+ if ((raddr.proto == AVAHI_PROTO_INET6 && RTA_PAYLOAD(a) != 16) ||
+ (raddr.proto == AVAHI_PROTO_INET && RTA_PAYLOAD(a) != 4))
+ return;
+
+ memcpy(raddr.data.data, RTA_DATA(a), RTA_PAYLOAD(a));
+ raddr_valid = 1;
+
+ break;
+
+ default:
+ ;
+ }
+
+ a = RTA_NEXT(a, l);
+ }
+
+ /* If there was no adress attached to this message, let's quit. */
+ if (!raddr_valid)
+ return;
+
+ if (n->nlmsg_type == RTM_NEWADDR) {
+ AvahiInterfaceAddress *addr;
+
+ /* This address is new or has been modified, so let's get an object for it */
+ if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr)))
+
+ /* Mmm, no object existing yet, so let's create a new one */
+ if (!(addr = avahi_interface_address_new(m, i, &raddr, ifaddrmsg->ifa_prefixlen)))
+ return; /* OOM */
+
+ /* Update the scope field for the address */
+ addr->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE;
+ } else {
+ AvahiInterfaceAddress *addr;
+ assert(n->nlmsg_type == RTM_DELADDR);
+
+ /* Try to get a reference to our AvahiInterfaceAddress object for this address */
+ if (!(addr = avahi_interface_monitor_get_address(m, i, &raddr)))
+ return;
+
+ /* And free it */
+ avahi_interface_address_free(addr);
+ }
+
+ /* Avahi only considers interfaces with at least one address
+ * attached relevant. Since we migh have added or removed an
+ * address, let's have it check again whether the interface is
+ * now relevant */
+ avahi_interface_check_relevant(i);
+
+ /* Update any associated RRs, like A or AAAA for our new/removed address */
+ avahi_interface_update_rrs(i, 0);
+
+ } else if (n->nlmsg_type == NLMSG_DONE) {
+
+ /* This wild dump request ended, so let's see what we do next */
+
+ if (m->osdep.list == LIST_IFACE) {
+
+ /* Mmmm, interfaces have been wild dumped already, so
+ * let's go on with wild dumping the addresses */
+
+ if (netlink_list_items(m->osdep.netlink, RTM_GETADDR, &m->osdep.query_addr_seq) < 0) {
+ avahi_log_warn("NETLINK: Failed to list addrs: %s", strerror(errno));
+ m->osdep.list = LIST_DONE;
+ } else
+
+ /* Update state information */
+ m->osdep.list = LIST_ADDR;
+
+ } else
+ /* We're done. Tell avahi_interface_monitor_sync() to finish. */
+ m->osdep.list = LIST_DONE;
+
+ if (m->osdep.list == LIST_DONE) {
+
+ /* Only after this boolean variable has been set, Avahi
+ * will start to announce or browse on all interfaces. It
+ * is originaly set to 0, which means that relevancy
+ * checks and RR updates are disabled during the wild
+ * dumps. */
+ m->list_complete = 1;
+
+ /* So let's check if any interfaces are relevant now */
+ avahi_interface_monitor_check_relevant(m);
+
+ /* And update all RRs attached to any interface */
+ avahi_interface_monitor_update_rrs(m, 0);
+
+ /* Tell the user that the wild dump is complete */
+ avahi_log_info("Network interface enumeration completed.");
+ }
+
+ } else if (n->nlmsg_type == NLMSG_ERROR &&
+ (n->nlmsg_seq == m->osdep.query_link_seq || n->nlmsg_seq == m->osdep.query_addr_seq)) {
+ struct nlmsgerr *e = NLMSG_DATA (n);
+
+ /* Some kind of error happened. Let's just tell the user and
+ * ignore it otherwise */
+
+ if (e->error)
+ avahi_log_warn("NETLINK: Failed to browse: %s", strerror(-e->error));
+ }
+}
+
+int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) {
+ assert(m);
+
+ /* Initialize our own data */
+
+ m->osdep.netlink = NULL;
+ m->osdep.query_addr_seq = m->osdep.query_link_seq = 0;
+
+ /* Create a netlink object for us. It abstracts some things and
+ * makes netlink easier to use. It will attach to the main loop
+ * for us and call netlink_callback() whenever an event
+ * happens. */
+ if (!(m->osdep.netlink = avahi_netlink_new(m->server->poll_api, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, netlink_callback, m)))
+ goto fail;
+
+ /* Set the initial state. */
+ m->osdep.list = LIST_IFACE;
+
+ /* Start the wild dump for the interfaces */
+ if (netlink_list_items(m->osdep.netlink, RTM_GETLINK, &m->osdep.query_link_seq) < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+
+ if (m->osdep.netlink) {
+ avahi_netlink_free(m->osdep.netlink);
+ m->osdep.netlink = NULL;
+ }
+
+ return -1;
+}
+
+void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {
+ assert(m);
+
+ if (m->osdep.netlink) {
+ avahi_netlink_free(m->osdep.netlink);
+ m->osdep.netlink = NULL;
+ }
+}
+
+void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
+ assert(m);
+
+ /* Let's handle netlink events until we are done with wild
+ * dumping */
+
+ while (!m->list_complete)
+ if (!avahi_netlink_work(m->osdep.netlink, 1) == 0)
+ break;
+
+ /* At this point Avahi knows about all local interfaces and
+ * addresses in existance. */
+}
diff --git a/trunk/avahi-core/iface-linux.h b/trunk/avahi-core/iface-linux.h
new file mode 100644
index 0000000..eed648c
--- /dev/null
+++ b/trunk/avahi-core/iface-linux.h
@@ -0,0 +1,42 @@
+#ifndef fooifacelinuxhfoo
+#define fooifacelinuxhfoo
+
+/* $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 AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep;
+
+#include "netlink.h"
+
+struct AvahiInterfaceMonitorOSDep {
+ AvahiNetlink *netlink;
+
+ unsigned query_addr_seq, query_link_seq;
+
+ enum {
+ LIST_IFACE,
+ LIST_ADDR,
+ LIST_DONE
+ } list;
+};
+
+
+#endif
diff --git a/trunk/avahi-core/iface-pfroute.c b/trunk/avahi-core/iface-pfroute.c
new file mode 100644
index 0000000..035e267
--- /dev/null
+++ b/trunk/avahi-core/iface-pfroute.c
@@ -0,0 +1,521 @@
+/* $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 <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#else
+#include <sys/sockio.h>
+#endif
+
+#include <net/route.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+
+#include "log.h"
+#include "iface.h"
+#include "iface-pfroute.h"
+#include "util.h"
+
+static int bitcount (unsigned int n)
+{
+ int count=0 ;
+ while (n)
+ {
+ count++ ;
+ n &= (n - 1) ;
+ }
+ return count ;
+}
+
+static void rtm_info(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m)
+{
+ AvahiHwInterface *hw;
+ struct if_msghdr *ifm = (struct if_msghdr *)rtm;
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)(ifm + 1);
+
+ if (sdl->sdl_family != AF_LINK)
+ return;
+
+ if (ifm->ifm_addrs == 0 && ifm->ifm_index > 0) {
+ if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) ifm->ifm_index)))
+ return;
+ avahi_hw_interface_free(hw, 0);
+ return;
+ }
+
+ if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifm->ifm_index)))
+ if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) ifm->ifm_index)))
+ return; /* OOM */
+
+ hw->flags_ok =
+ (ifm->ifm_flags & IFF_UP) &&
+ (!m->server->config.use_iff_running || (ifm->ifm_flags & IFF_RUNNING)) &&
+ !(ifm->ifm_flags & IFF_LOOPBACK) &&
+ (ifm->ifm_flags & IFF_MULTICAST) &&
+ (m->server->config.allow_point_to_point || !(ifm->ifm_flags & IFF_POINTOPOINT));
+
+ avahi_free(hw->name);
+ hw->name = avahi_strndup(sdl->sdl_data, sdl->sdl_nlen);
+
+ hw->mtu = ifm->ifm_data.ifi_mtu;
+
+ hw->mac_address_size = sdl->sdl_alen;
+ if (hw->mac_address_size > AVAHI_MAC_ADDRESS_MAX)
+ hw->mac_address_size = AVAHI_MAC_ADDRESS_MAX;
+
+ memcpy(hw->mac_address, sdl->sdl_data + sdl->sdl_nlen, hw->mac_address_size);
+
+/* { */
+/* char mac[256]; */
+/* avahi_log_debug("======\n name: %s\n index:%d\n mtu:%d\n mac:%s\n flags_ok:%d\n======", */
+/* hw->name, hw->index, */
+/* hw->mtu, */
+/* avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size), */
+/* hw->flags_ok); */
+/* } */
+
+ avahi_hw_interface_check_relevant(hw);
+ avahi_hw_interface_update_rrs(hw, 0);
+}
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#ifdef HAVE_SYS_SYSCTL_H
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+#else
+#define ADVANCE(x, n) (x += ROUNDUP(sizeof(struct sockaddr)))
+#endif
+
+static void rtm_addr(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m)
+{
+ AvahiInterface *iface;
+ AvahiAddress raddr;
+ int raddr_valid = 0;
+ struct ifa_msghdr *ifam = (struct ifa_msghdr *) rtm;
+ char *cp = (char *)(ifam + 1);
+ int addrs = ifam->ifam_addrs;
+ int i;
+ int prefixlen = 0;
+ struct sockaddr *sa =NULL;
+
+#if defined(__NetBSD__) || defined(__OpenBSD__)
+ if(((struct sockaddr *)cp)->sa_family == AF_UNSPEC)
+ ((struct sockaddr *)cp)->sa_family = AF_INET;
+#endif
+
+ if(((struct sockaddr *)cp)->sa_family != AF_INET && ((struct sockaddr *)cp)->sa_family != AF_INET6)
+ return;
+
+ if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex) ifam->ifam_index, avahi_af_to_proto(((struct sockaddr *)cp)->sa_family))))
+ return;
+
+ raddr.proto = avahi_af_to_proto(((struct sockaddr *)cp)->sa_family);
+
+ for(i = 0; addrs != 0 && i < RTAX_MAX; addrs &= ~(1<<i), i++)
+ {
+ if (!(addrs & 1<<i))
+ continue;
+ sa = (struct sockaddr *)cp;
+#ifdef HAVE_SYS_SYSCTL_H
+ if (sa->sa_len == 0)
+ continue;
+#endif
+ switch(sa->sa_family) {
+ case AF_INET:
+ switch (1<<i) {
+ case RTA_NETMASK:
+ prefixlen = bitcount((unsigned int)((struct sockaddr_in *)sa)->sin_addr.s_addr);
+ break;
+ case RTA_IFA:
+ memcpy(raddr.data.data, &((struct sockaddr_in *)sa)->sin_addr, sizeof(struct in_addr));
+ raddr_valid = 1;
+ default:
+ break;
+ }
+ break;
+ case AF_INET6:
+ switch (1<<i) {
+ case RTA_NETMASK:
+ prefixlen = bitcount((unsigned int)((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr);
+ break;
+ case RTA_IFA:
+ memcpy(raddr.data.data, &((struct sockaddr_in6 *)sa)->sin6_addr, sizeof(struct in6_addr));
+ raddr_valid = 1;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+#ifdef SA_SIZE
+ cp += SA_SIZE(sa);
+#else
+ ADVANCE(cp, sa);
+#endif
+ }
+
+ if (!raddr_valid)
+ return;
+
+ if(rtm->rtm_type == RTM_NEWADDR)
+ {
+ AvahiInterfaceAddress *addriface;
+ if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr)))
+ if (!(addriface = avahi_interface_address_new(m, iface, &raddr, prefixlen)))
+ return; /* OOM */
+ /* FIXME */
+ /* addriface->global_scope = ifaddrmsg->ifa_scope == RT_SCOPE_UNIVERSE || ifaddrmsg->ifa_scope == RT_SCOPE_SITE; */
+ addriface->global_scope = 1;
+ }
+ else
+ {
+ AvahiInterfaceAddress *addriface;
+ assert(rtm->rtm_type == RTM_DELADDR);
+ if (!(addriface = avahi_interface_monitor_get_address(m, iface, &raddr)))
+ return;
+ avahi_interface_address_free(addriface);
+ }
+
+ avahi_interface_check_relevant(iface);
+ avahi_interface_update_rrs(iface, 0);
+}
+
+static void parse_rtmsg(struct rt_msghdr *rtm, AvahiInterfaceMonitor *m)
+{
+ assert(m);
+ assert(rtm);
+
+ if (rtm->rtm_version != RTM_VERSION) {
+ avahi_log_warn("routing message version %d not understood",
+ rtm->rtm_version);
+ return;
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_IFINFO:
+ rtm_info(rtm,m);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ rtm_addr(rtm,m);
+ break;
+ default:
+ break;
+ }
+}
+
+static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event,void *userdata) {
+ AvahiInterfaceMonitor *m = (AvahiInterfaceMonitor *)userdata;
+ AvahiPfRoute *nl = m->osdep.pfroute;
+ ssize_t bytes;
+ char msg[2048];
+
+ assert(m);
+ assert(w);
+ assert(nl);
+ assert(fd == nl->fd);
+
+ do {
+ if((bytes = recv(nl->fd, msg, 2048, MSG_DONTWAIT)) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return;
+ avahi_log_error(__FILE__": recv() failed: %s", strerror(errno));
+ return;
+ }
+ parse_rtmsg((struct rt_msghdr *)msg, m);
+ }
+ while (bytes > 0);
+}
+
+int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m) {
+ int fd = -1;
+ m->osdep.pfroute = NULL;
+
+ assert(m);
+
+ if ((fd = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC)) < 0) {
+ avahi_log_error(__FILE__": socket(PF_ROUTE): %s", strerror(errno));
+ goto fail;
+ }
+
+ if (!(m->osdep.pfroute = avahi_new(AvahiPfRoute , 1))) {
+ avahi_log_error(__FILE__": avahi_new() failed.");
+ goto fail;
+ }
+ m->osdep.pfroute->fd = fd;
+
+ if (!(m->osdep.pfroute->watch = m->server->poll_api->watch_new(m->server->poll_api,
+ m->osdep.pfroute->fd,
+ AVAHI_WATCH_IN,
+ socket_event,
+ m))) {
+ avahi_log_error(__FILE__": Failed to create watch.");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+
+ if (m->osdep.pfroute) {
+ if (m->osdep.pfroute->watch)
+ m->server->poll_api->watch_free(m->osdep.pfroute->watch);
+
+ if (fd >= 0)
+ close(fd);
+
+ m->osdep.pfroute = NULL;
+ }
+
+ return -1;
+}
+
+void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m) {
+ assert(m);
+
+ if (m->osdep.pfroute) {
+ if (m->osdep.pfroute->watch)
+ m->server->poll_api->watch_free(m->osdep.pfroute->watch);
+
+ if (m->osdep.pfroute->fd >= 0)
+ close(m->osdep.pfroute->fd);
+
+ avahi_free(m->osdep.pfroute);
+ m->osdep.pfroute = NULL;
+ }
+}
+
+#if defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */
+/*
+ * I got this function from GNU zsbra
+ */
+static int ip6_masklen (struct in6_addr netmask) {
+ int len = 0;
+ unsigned char val;
+ unsigned char *pnt;
+
+ pnt = (unsigned char *) & netmask;
+
+ while ((*pnt == 0xff) && len < 128) {
+ len += 8;
+ pnt++;
+ }
+
+ if (len < 128) {
+ val = *pnt;
+ while (val) {
+ len++;
+ val <<= 1;
+ }
+ }
+ return len;
+}
+
+static void if_add_interface(struct lifreq *lifreq, AvahiInterfaceMonitor *m, int fd, int count)
+{
+ AvahiHwInterface *hw;
+ AvahiAddress addr;
+ struct lifreq lifrcopy;
+ unsigned int index;
+ int flags;
+ int mtu;
+ int prefixlen;
+ AvahiInterfaceAddress *addriface;
+ AvahiInterface *iface;
+ struct sockaddr_in mask;
+ struct sockaddr_in6 mask6;
+ char caddr[AVAHI_ADDRESS_STR_MAX];
+
+ lifrcopy = *lifreq;
+
+ if (ioctl(fd, SIOCGLIFFLAGS, &lifrcopy) < 0) {
+ avahi_log_error(__FILE__": ioctl(SIOCGLIFFLAGS) %s", strerror(errno));
+ return;
+ }
+ flags = lifrcopy.lifr_flags;
+
+ if (ioctl(fd, SIOCGLIFMTU, &lifrcopy) < 0) {
+ avahi_log_error(__FILE__": ioctl(SIOCGLIFMTU) %s", strerror(errno));
+ return;
+ }
+ mtu = lifrcopy.lifr_metric;
+
+ if (ioctl(fd, SIOCGLIFADDR, &lifrcopy) < 0) {
+ avahi_log_error(__FILE__": ioctl(SIOCGLIFADDR) %s", strerror(errno));
+ return;
+ }
+ addr.proto = avahi_af_to_proto(lifreq->lifr_addr.ss_family);
+ if (ioctl(fd, SIOCGLIFNETMASK, &lifrcopy) < 0) {
+ avahi_log_error(__FILE__": ioctl(SIOCGLIFNETMASK) %s", strerror(errno));
+ return;
+ }
+ switch (lifreq->lifr_addr.ss_family) {
+ case AF_INET:
+ memcpy(addr.data.data, &((struct sockaddr_in *)&lifreq->lifr_addr)->sin_addr, sizeof(struct in_addr));
+ memcpy(&mask, &((struct sockaddr_in *)&lifrcopy.lifr_addr)->sin_addr, sizeof(struct in_addr));
+ prefixlen = bitcount((unsigned int) mask.sin_addr.s_addr);
+ break;
+ case AF_INET6:
+ memcpy(addr.data.data, &((struct sockaddr_in6 *)&lifreq->lifr_addr)->sin6_addr, sizeof(struct in6_addr));
+ memcpy(&mask6, &((struct sockaddr_in6 *)&lifrcopy.lifr_addr)->sin6_addr, sizeof(struct in6_addr));
+ prefixlen = lifrcopy.lifr_addrlen;
+ break;
+ default:
+ break;
+ }
+ index = if_nametoindex(lifreq->lifr_name);
+
+ if (!(hw = avahi_interface_monitor_get_hw_interface(m, (AvahiIfIndex) index))) {
+ if (!(hw = avahi_hw_interface_new(m, (AvahiIfIndex) index)))
+ return; /* OOM */
+
+ hw->flags_ok =
+ (flags & IFF_UP) &&
+ (!m->server->config.use_iff_running || (flags & IFF_RUNNING)) &&
+ !(flags & IFF_LOOPBACK) &&
+ (flags & IFF_MULTICAST) &&
+ (m->server->config.allow_point_to_point || !(flags & IFF_POINTOPOINT));
+ hw->name = avahi_strdup(lifreq->lifr_name);
+ hw->mtu = mtu;
+ /* TODO get mac address */
+ }
+
+ if (!(iface = avahi_interface_monitor_get_interface(m, (AvahiIfIndex)index, addr.proto)))
+ return;
+
+ if (!(addriface = avahi_interface_monitor_get_address(m, iface, &addr)))
+ if (!(addriface = avahi_interface_address_new(m, iface, &addr, prefixlen)))
+ return; /* OOM */
+
+ addriface->global_scope = 1;
+
+ avahi_hw_interface_check_relevant(hw);
+ avahi_hw_interface_update_rrs(hw, 0);
+}
+#endif
+
+void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
+#ifndef HAVE_STRUCT_LIFCONF
+ size_t needed;
+ int mib[6];
+ char *buf, *lim, *next, count = 0;
+ struct rt_msghdr *rtm;
+
+ assert(m);
+
+ retry2:
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0; /* protocol */
+ mib[3] = 0; /* wildcard address family */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0; /* no flags */
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ {
+ avahi_log_error("sysctl failed: %s", strerror(errno));
+ avahi_log_error("route-sysctl-estimate");
+ return;
+ }
+ if ((buf = avahi_malloc(needed)) == NULL)
+ {
+ avahi_log_error("malloc failed in avahi_interface_monitor_sync");
+ return;
+ }
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ avahi_log_warn("sysctl failed: %s", strerror(errno));
+ if (errno == ENOMEM && count++ < 10) {
+ avahi_log_warn("Routing table grew, retrying");
+ sleep(1);
+ avahi_free(buf);
+ goto retry2;
+ }
+ }
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ parse_rtmsg(rtm, m);
+ }
+
+ m->list_complete = 1;
+ avahi_interface_monitor_check_relevant(m);
+ avahi_interface_monitor_update_rrs(m, 0);
+ avahi_log_info("Network interface enumeration completed.");
+#elif defined (SIOCGLIFNUM) && defined(HAVE_STRUCT_LIFCONF) /* Solaris 8 and later; Sol 7? */
+ int sockfd;
+ int ret;
+ int n;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq *lifreq;
+
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ avahi_log_error(__FILE__": socket(PFROUTE): %s", strerror(errno));
+ return;
+ }
+ lifc.lifc_buf = NULL;
+ lifn.lifn_family = AF_UNSPEC;
+ lifn.lifn_flags = 0;
+ if (ioctl(sockfd, SIOCGLIFNUM, &lifn) < 0) {
+ avahi_log_error(__FILE__": ioctl(SIOCGLIFNUM): %s", strerror(errno));
+ goto end;
+ }
+ lifc.lifc_len = lifn.lifn_count * sizeof (struct lifreq);
+ if ((lifc.lifc_buf = avahi_malloc(lifc.lifc_len)) == NULL) {
+ avahi_log_error("malloc failed in avahi_interface_monitor_sync");
+ goto end;
+ }
+ lifc.lifc_family = NULL;
+ lifc.lifc_flags = 0;
+ if(ioctl(sockfd, SIOCGLIFCONF, &lifc) < 0) {
+ avahi_log_error(__FILE__": ioctl(SIOCGLIFCONF): %s", strerror(errno));
+ goto end;
+ }
+ lifreq = lifc.lifc_req;
+
+ for (n = 0; n < lifc.lifc_len; n += sizeof(struct lifreq)) {
+ if_add_interface(lifreq, m, sockfd, lifn.lifn_count);
+ lifreq++;
+ }
+ m->list_complete = 1;
+ avahi_interface_monitor_check_relevant(m);
+ avahi_interface_monitor_update_rrs(m, 0);
+end:
+ close(sockfd);
+ avahi_free(lifc.lifc_buf);
+
+ avahi_log_info("Network interface enumeration completed.");
+#endif
+}
diff --git a/trunk/avahi-core/iface-pfroute.h b/trunk/avahi-core/iface-pfroute.h
new file mode 100644
index 0000000..34098da
--- /dev/null
+++ b/trunk/avahi-core/iface-pfroute.h
@@ -0,0 +1,39 @@
+#ifndef fooifacepfroutehfoo
+#define fooifacepfroutehfoo
+
+/* $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 <avahi-common/watch.h>
+
+typedef struct AvahiPfRoute AvahiPfRoute;
+struct AvahiPfRoute {
+ int fd;
+ AvahiWatch *watch;
+ AvahiInterfaceMonitor *m;
+};
+
+typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep;
+
+struct AvahiInterfaceMonitorOSDep {
+ AvahiPfRoute *pfroute;
+};
+
+#endif
diff --git a/trunk/avahi-core/iface.c b/trunk/avahi-core/iface.c
new file mode 100644
index 0000000..5685618
--- /dev/null
+++ b/trunk/avahi-core/iface.c
@@ -0,0 +1,812 @@
+/* $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 <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <avahi-common/error.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/domain.h>
+
+#include "iface.h"
+#include "dns.h"
+#include "socket.h"
+#include "announce.h"
+#include "util.h"
+#include "log.h"
+#include "multicast-lookup.h"
+#include "querier.h"
+
+void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs) {
+ AvahiInterfaceMonitor *m;
+
+ assert(a);
+ m = a->monitor;
+
+ if (a->interface->announcing &&
+ m->list_complete &&
+ avahi_interface_address_is_relevant(a) &&
+ !remove_rrs &&
+ m->server->config.publish_addresses &&
+ (m->server->state == AVAHI_SERVER_RUNNING ||
+ m->server->state == AVAHI_SERVER_REGISTERING)) {
+
+ /* Fill the entry group */
+ if (!a->entry_group)
+ a->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
+
+ if (!a->entry_group) /* OOM */
+ return;
+
+ if (avahi_s_entry_group_is_empty(a->entry_group)) {
+ char t[AVAHI_ADDRESS_STR_MAX];
+ avahi_address_snprint(t, sizeof(t), &a->address);
+
+ avahi_log_info("Registering new address record for %s on %s.", t, a->interface->hardware->name);
+
+ if (avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, a->interface->protocol, 0, NULL, &a->address) < 0) {
+ avahi_log_warn(__FILE__": avahi_server_add_address() failed: %s", avahi_strerror(m->server->error));
+ avahi_s_entry_group_free(a->entry_group);
+ a->entry_group = NULL;
+ return;
+ }
+
+ avahi_s_entry_group_commit(a->entry_group);
+ }
+ } else {
+
+ /* Clear the entry group */
+
+ if (a->entry_group && !avahi_s_entry_group_is_empty(a->entry_group)) {
+ char t[AVAHI_ADDRESS_STR_MAX];
+ avahi_address_snprint(t, sizeof(t), &a->address);
+
+ if (avahi_s_entry_group_get_state(a->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING &&
+ m->server->state == AVAHI_SERVER_REGISTERING)
+ avahi_server_decrease_host_rr_pending(m->server);
+
+ avahi_log_info("Withdrawing address record for %s on %s.", t, a->interface->hardware->name);
+
+ avahi_s_entry_group_reset(a->entry_group);
+ }
+ }
+}
+
+void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs) {
+ AvahiInterfaceAddress *a;
+
+ assert(i);
+
+ for (a = i->addresses; a; a = a->address_next)
+ avahi_interface_address_update_rrs(a, remove_rrs);
+}
+
+void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs) {
+ AvahiInterface *i;
+ AvahiInterfaceMonitor *m;
+
+ assert(hw);
+ m = hw->monitor;
+
+ for (i = hw->interfaces; i; i = i->by_hardware_next)
+ avahi_interface_update_rrs(i, remove_rrs);
+
+ if (m->list_complete &&
+ !remove_rrs &&
+ m->server->config.publish_workstation &&
+ (m->server->state == AVAHI_SERVER_RUNNING)) {
+
+ if (!hw->entry_group)
+ hw->entry_group = avahi_s_entry_group_new(m->server, avahi_host_rr_entry_group_callback, NULL);
+
+ if (!hw->entry_group)
+ return; /* OOM */
+
+ if (avahi_s_entry_group_is_empty(hw->entry_group)) {
+ char name[AVAHI_LABEL_MAX], mac[256];
+
+ avahi_format_mac_address(mac, sizeof(mac), hw->mac_address, hw->mac_address_size);
+ snprintf(name, sizeof(name), "%s [%s]", m->server->host_name, mac);
+
+ if (avahi_server_add_service(m->server, hw->entry_group, hw->index, AVAHI_PROTO_UNSPEC, 0, name, "_workstation._tcp", NULL, NULL, 9, NULL) < 0) {
+ avahi_log_warn(__FILE__": avahi_server_add_service() failed: %s", avahi_strerror(m->server->error));
+ avahi_s_entry_group_free(hw->entry_group);
+ hw->entry_group = NULL;
+ } else
+ avahi_s_entry_group_commit(hw->entry_group);
+ }
+
+ } else {
+
+ if (hw->entry_group && !avahi_s_entry_group_is_empty(hw->entry_group)) {
+
+ if (avahi_s_entry_group_get_state(hw->entry_group) == AVAHI_ENTRY_GROUP_REGISTERING)
+ avahi_server_decrease_host_rr_pending(m->server);
+
+ avahi_s_entry_group_reset(hw->entry_group);
+ }
+ }
+}
+
+void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs) {
+ AvahiHwInterface *hw;
+
+ assert(m);
+
+ for (hw = m->hw_interfaces; hw; hw = hw->hardware_next)
+ avahi_hw_interface_update_rrs(hw, remove_rrs);
+}
+
+static int interface_mdns_mcast_join(AvahiInterface *i, int join) {
+ char at[AVAHI_ADDRESS_STR_MAX];
+ int r;
+ assert(i);
+
+ if (!!join == !!i->mcast_joined)
+ return 0;
+
+ if (join) {
+ AvahiInterfaceAddress *a;
+
+ /* Look if there's an address with global scope */
+ for (a = i->addresses; a; a = a->address_next)
+ if (a->global_scope)
+ break;
+
+ /* No address with a global scope has been found, so let's use
+ * any. */
+ if (!a)
+ a = i->addresses;
+
+ /* Hmm, there is no address available. */
+ if (!a) {
+ avahi_log_warn(__FILE__": interface_mdns_mcast_join() called but no local address available.");
+ return -1;
+ }
+
+ i->local_mcast_address = a->address;
+ }
+
+ avahi_log_info("%s mDNS multicast group on interface %s.%s with address %s.",
+ join ? "Joining" : "Leaving",
+ i->hardware->name,
+ avahi_proto_to_string(i->protocol),
+ avahi_address_snprint(at, sizeof(at), &i->local_mcast_address));
+
+ if (i->protocol == AVAHI_PROTO_INET6)
+ r = avahi_mdns_mcast_join_ipv6(i->monitor->server->fd_ipv6, &i->local_mcast_address.data.ipv6, i->hardware->index, join);
+ else {
+ assert(i->protocol == AVAHI_PROTO_INET);
+
+ r = avahi_mdns_mcast_join_ipv4(i->monitor->server->fd_ipv4, &i->local_mcast_address.data.ipv4, i->hardware->index, join);
+ }
+
+ if (r < 0)
+ i->mcast_joined = 0;
+ else
+ i->mcast_joined = join;
+
+ return 0;
+}
+
+static int interface_mdns_mcast_rejoin(AvahiInterface *i) {
+ AvahiInterfaceAddress *a, *usable = NULL, *found = NULL;
+ assert(i);
+
+ if (!i->mcast_joined)
+ return 0;
+
+ /* Check whether old address we joined with is still available. If
+ * not, rejoin using an other address. */
+
+ for (a = i->addresses; a; a = a->address_next) {
+ if (a->global_scope && !usable)
+ usable = a;
+
+ if (avahi_address_cmp(&a->address, &i->local_mcast_address) == 0) {
+
+ if (a->global_scope)
+ /* No action necessary: the address still exists and
+ * has global scope. */
+ return 0;
+
+ found = a;
+ }
+ }
+
+ if (found && !usable)
+ /* No action necessary: the address still exists and no better one has been found */
+ return 0;
+
+ interface_mdns_mcast_join(i, 0);
+ return interface_mdns_mcast_join(i, 1);
+}
+
+void avahi_interface_address_free(AvahiInterfaceAddress *a) {
+ assert(a);
+ assert(a->interface);
+
+ avahi_interface_address_update_rrs(a, 1);
+ AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a);
+
+ if (a->entry_group)
+ avahi_s_entry_group_free(a->entry_group);
+
+ interface_mdns_mcast_rejoin(a->interface);
+
+ avahi_free(a);
+}
+
+void avahi_interface_free(AvahiInterface *i, int send_goodbye) {
+ assert(i);
+
+ /* Handle goodbyes and remove announcers */
+ avahi_goodbye_interface(i->monitor->server, i, send_goodbye, 1);
+ avahi_response_scheduler_force(i->response_scheduler);
+ assert(!i->announcers);
+
+ if (i->mcast_joined)
+ interface_mdns_mcast_join(i, 0);
+
+ /* Remove queriers */
+ avahi_querier_free_all(i);
+ avahi_hashmap_free(i->queriers_by_key);
+
+ /* Remove local RRs */
+ avahi_interface_update_rrs(i, 1);
+
+ while (i->addresses)
+ avahi_interface_address_free(i->addresses);
+
+ avahi_response_scheduler_free(i->response_scheduler);
+ avahi_query_scheduler_free(i->query_scheduler);
+ avahi_probe_scheduler_free(i->probe_scheduler);
+ avahi_cache_free(i->cache);
+
+ AVAHI_LLIST_REMOVE(AvahiInterface, interface, i->monitor->interfaces, i);
+ AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i);
+
+ avahi_free(i);
+}
+
+void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye) {
+ assert(hw);
+
+ avahi_hw_interface_update_rrs(hw, 1);
+
+ while (hw->interfaces)
+ avahi_interface_free(hw->interfaces, send_goodbye);
+
+ if (hw->entry_group)
+ avahi_s_entry_group_free(hw->entry_group);
+
+ AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, hw->monitor->hw_interfaces, hw);
+ avahi_hashmap_remove(hw->monitor->hashmap, &hw->index);
+
+ avahi_free(hw->name);
+ avahi_free(hw);
+}
+
+AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol) {
+ AvahiInterface *i;
+
+ assert(m);
+ assert(hw);
+ assert(AVAHI_PROTO_VALID(protocol));
+
+ if (!(i = avahi_new(AvahiInterface, 1)))
+ goto fail; /* OOM */
+
+ i->monitor = m;
+ i->hardware = hw;
+ i->protocol = protocol;
+ i->announcing = 0;
+ i->mcast_joined = 0;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses);
+ AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, i->announcers);
+
+ 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);
+ i->probe_scheduler = avahi_probe_scheduler_new(i);
+
+ if (!i->cache || !i->response_scheduler || !i->query_scheduler || !i->probe_scheduler)
+ goto fail; /* OOM */
+
+ AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i);
+ AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i);
+
+ return i;
+
+fail:
+
+ if (i) {
+ if (i->cache)
+ avahi_cache_free(i->cache);
+ if (i->response_scheduler)
+ avahi_response_scheduler_free(i->response_scheduler);
+ if (i->query_scheduler)
+ avahi_query_scheduler_free(i->query_scheduler);
+ if (i->probe_scheduler)
+ avahi_probe_scheduler_free(i->probe_scheduler);
+ }
+
+ return NULL;
+}
+
+AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx) {
+ AvahiHwInterface *hw;
+
+ assert(m);
+ assert(AVAHI_IF_VALID(idx));
+
+ if (!(hw = avahi_new(AvahiHwInterface, 1)))
+ return NULL;
+
+ hw->monitor = m;
+ hw->name = NULL;
+ hw->flags_ok = 0;
+ hw->mtu = 1500;
+ hw->index = idx;
+ hw->mac_address_size = 0;
+ hw->entry_group = NULL;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces);
+ AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw);
+
+ avahi_hashmap_insert(m->hashmap, &hw->index, hw);
+
+ if (m->server->fd_ipv4 >= 0)
+ avahi_interface_new(m, hw, AVAHI_PROTO_INET);
+ if (m->server->fd_ipv6 >= 0)
+ avahi_interface_new(m, hw, AVAHI_PROTO_INET6);
+
+ return hw;
+}
+
+AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len) {
+ AvahiInterfaceAddress *a;
+
+ assert(m);
+ assert(i);
+
+ if (!(a = avahi_new(AvahiInterfaceAddress, 1)))
+ return NULL;
+
+ a->interface = i;
+ a->monitor = m;
+ a->address = *addr;
+ a->prefix_len = prefix_len;
+ a->global_scope = 0;
+ a->entry_group = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, a);
+
+ return a;
+}
+
+void avahi_interface_check_relevant(AvahiInterface *i) {
+ int b;
+ AvahiInterfaceMonitor *m;
+
+ assert(i);
+ m = i->monitor;
+
+ b = avahi_interface_is_relevant(i);
+
+ if (m->list_complete && b && !i->announcing) {
+ avahi_log_info("New relevant interface %s.%s for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol));
+
+ interface_mdns_mcast_join(i, 1);
+
+ i->announcing = 1;
+ avahi_announce_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 for mDNS.", i->hardware->name, avahi_proto_to_string(i->protocol));
+
+ interface_mdns_mcast_join(i, 0);
+
+ avahi_goodbye_interface(m->server, i, 0, 1);
+ 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);
+ avahi_cache_flush(i->cache);
+
+ i->announcing = 0;
+
+ } else
+ interface_mdns_mcast_rejoin(i);
+}
+
+void avahi_hw_interface_check_relevant(AvahiHwInterface *hw) {
+ AvahiInterface *i;
+
+ assert(hw);
+
+ for (i = hw->interfaces; i; i = i->by_hardware_next)
+ avahi_interface_check_relevant(i);
+}
+
+void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m) {
+ AvahiInterface *i;
+
+ assert(m);
+
+ for (i = m->interfaces; i; i = i->interface_next)
+ avahi_interface_check_relevant(i);
+}
+
+AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) {
+ AvahiInterfaceMonitor *m = NULL;
+
+ if (!(m = avahi_new0(AvahiInterfaceMonitor, 1)))
+ return NULL; /* OOM */
+
+ m->server = s;
+ m->list_complete = 0;
+ m->hashmap = avahi_hashmap_new(avahi_int_hash, avahi_int_equal, NULL, NULL);
+
+ AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces);
+ AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces);
+
+ if (avahi_interface_monitor_init_osdep(m) < 0)
+ goto fail;
+
+ return m;
+
+fail:
+ avahi_interface_monitor_free(m);
+ return NULL;
+}
+
+void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) {
+ assert(m);
+
+ while (m->hw_interfaces)
+ avahi_hw_interface_free(m->hw_interfaces, 1);
+
+ assert(!m->interfaces);
+
+ avahi_interface_monitor_free_osdep(m);
+
+ if (m->hashmap)
+ avahi_hashmap_free(m->hashmap);
+
+ avahi_free(m);
+}
+
+
+AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol) {
+ AvahiHwInterface *hw;
+ AvahiInterface *i;
+
+ assert(m);
+ assert(idx >= 0);
+ assert(protocol != AVAHI_PROTO_UNSPEC);
+
+ if (!(hw = avahi_interface_monitor_get_hw_interface(m, idx)))
+ return NULL;
+
+ for (i = hw->interfaces; i; i = i->by_hardware_next)
+ if (i->protocol == protocol)
+ return i;
+
+ return NULL;
+}
+
+AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx) {
+ assert(m);
+ assert(idx > 0);
+
+ return avahi_hashmap_lookup(m->hashmap, &idx);
+}
+
+AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) {
+ AvahiInterfaceAddress *ia;
+
+ assert(m);
+ assert(i);
+ assert(raddr);
+
+ for (ia = i->addresses; ia; ia = ia->address_next)
+ if (avahi_address_cmp(&ia->address, raddr) == 0)
+ return ia;
+
+ return NULL;
+}
+
+void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port) {
+ assert(i);
+ assert(p);
+
+ if (!avahi_interface_is_relevant(i))
+ return;
+
+ assert(!a || a->proto == i->protocol);
+
+ if (i->protocol == AVAHI_PROTO_INET && i->monitor->server->fd_ipv4 >= 0)
+ avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv4 : NULL, a ? &a->data.ipv4 : NULL, port);
+ else if (i->protocol == AVAHI_PROTO_INET6 && i->monitor->server->fd_ipv6 >= 0)
+ avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p, i->mcast_joined ? &i->local_mcast_address.data.ipv6 : NULL, a ? &a->data.ipv6 : NULL, port);
+}
+
+void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) {
+ assert(i);
+ assert(p);
+
+ avahi_interface_send_packet_unicast(i, p, NULL, 0);
+}
+
+int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately, unsigned *ret_id) {
+ assert(i);
+ assert(key);
+
+ if (avahi_interface_is_relevant(i))
+ return avahi_query_scheduler_post(i->query_scheduler, key, immediately, ret_id);
+
+ return 0;
+}
+
+int avahi_interface_withraw_query(AvahiInterface *i, unsigned id) {
+
+ return avahi_query_scheduler_withdraw_by_id(i->query_scheduler, id);
+}
+
+int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) {
+ assert(i);
+ assert(record);
+
+ if (avahi_interface_is_relevant(i))
+ return avahi_response_scheduler_post(i->response_scheduler, record, flush_cache, querier, immediately);
+
+ return 0;
+}
+
+int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, int immediately) {
+ assert(i);
+ assert(record);
+
+ if (avahi_interface_is_relevant(i))
+ return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately);
+
+ return 0;
+}
+
+int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata) {
+ AvahiInterface *i;
+ assert(m);
+
+ for (i = m->interfaces; i; i = i->interface_next) {
+ if (avahi_interface_is_relevant(i)) {
+ char ln[256];
+ snprintf(ln, sizeof(ln), ";;; INTERFACE %s.%s ;;;", i->hardware->name, avahi_proto_to_string(i->protocol));
+ callback(ln, userdata);
+ if (avahi_cache_dump(i->cache, callback, userdata) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int avahi_interface_is_relevant(AvahiInterface *i) {
+ AvahiInterfaceAddress *a;
+ int relevant_address;
+
+ assert(i);
+
+ relevant_address = 0;
+
+ for (a = i->addresses; a; a = a->address_next)
+ if (avahi_interface_address_is_relevant(a)) {
+ relevant_address = 1;
+ break;
+ }
+
+ return i->hardware->flags_ok && relevant_address;
+}
+
+int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a) {
+ AvahiInterfaceAddress *b;
+ assert(a);
+
+ /* Publish public IP addresses */
+ if (a->global_scope)
+ return 1;
+ else {
+
+ /* Publish link local IP addresses if they are the only ones on the link */
+ for (b = a->interface->addresses; b; b = b->address_next) {
+ if (b == a)
+ continue;
+
+ if (b->global_scope)
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol) {
+ assert(i);
+
+ if (idx != AVAHI_IF_UNSPEC && idx != i->hardware->index)
+ return 0;
+
+ if (protocol != AVAHI_PROTO_UNSPEC && protocol != i->protocol)
+ return 0;
+
+ return 1;
+}
+
+void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex interface, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata) {
+ assert(m);
+ assert(callback);
+
+ if (interface != AVAHI_IF_UNSPEC) {
+ if (protocol != AVAHI_PROTO_UNSPEC) {
+ AvahiInterface *i;
+
+ if ((i = avahi_interface_monitor_get_interface(m, interface, protocol)))
+ callback(m, i, userdata);
+
+ } else {
+ AvahiHwInterface *hw;
+ AvahiInterface *i;
+
+ if ((hw = avahi_interface_monitor_get_hw_interface(m, interface)))
+ for (i = hw->interfaces; i; i = i->by_hardware_next)
+ if (avahi_interface_match(i, interface, protocol))
+ callback(m, i, userdata);
+ }
+
+ } else {
+ AvahiInterface *i;
+
+ for (i = m->interfaces; i; i = i->interface_next)
+ if (avahi_interface_match(i, interface, protocol))
+ callback(m, i, userdata);
+ }
+}
+
+
+int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a) {
+ AvahiInterface *i;
+ AvahiInterfaceAddress *ia;
+ assert(m);
+ assert(a);
+
+ for (i = m->interfaces; i; i = i->interface_next)
+ for (ia = i->addresses; ia; ia = ia->address_next)
+ if (avahi_address_cmp(a, &ia->address) == 0)
+ return 1;
+
+ return 0;
+}
+
+int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a) {
+ AvahiInterfaceAddress *ia;
+
+ assert(i);
+ assert(a);
+
+ if (a->proto != i->protocol)
+ return 0;
+
+ for (ia = i->addresses; ia; ia = ia->address_next) {
+
+ if (a->proto == AVAHI_PROTO_INET) {
+ uint32_t m;
+
+ m = ~(((uint32_t) -1) >> ia->prefix_len);
+
+ if ((ntohl(a->data.ipv4.address) & m) == (ntohl(ia->address.data.ipv4.address) & m))
+ return 1;
+ } else {
+ unsigned j;
+ unsigned char pl;
+ assert(a->proto == AVAHI_PROTO_INET6);
+
+ pl = ia->prefix_len;
+
+ for (j = 0; j < 16; j++) {
+ uint8_t m;
+
+ if (pl == 0)
+ return 1;
+
+ if (pl >= 8) {
+ m = 0xFF;
+ pl -= 8;
+ } else {
+ m = ~(0xFF >> pl);
+ pl = 0;
+ }
+
+ if ((a->data.ipv6.address[j] & m) != (ia->address.data.ipv6.address[j] & m))
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a) {
+ AvahiInterface *i;
+ AvahiInterfaceAddress *j;
+
+ assert(m);
+ assert(iface != AVAHI_IF_UNSPEC);
+ assert(a);
+
+ if (!(i = avahi_interface_monitor_get_interface(m, iface, a->proto)))
+ return 0;
+
+ for (j = i->addresses; j; j = j->address_next)
+ if (avahi_address_cmp(a, &j->address) == 0)
+ return 1;
+
+ return 0;
+}
+
+AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a) {
+ AvahiInterface *i;
+ assert(m);
+
+ /* Some stupid OS don't support passing the interface index when a
+ * packet is recieved. We have to work around that limitation by
+ * looking for an interface that has the incoming address
+ * attached. This is sometimes ambiguous, but we have to live with
+ * it. */
+
+ for (i = m->interfaces; i; i = i->interface_next) {
+ AvahiInterfaceAddress *ai;
+
+ if (i->protocol != a->proto)
+ continue;
+
+ for (ai = i->addresses; ai; ai = ai->address_next)
+ if (avahi_address_cmp(a, &ai->address) == 0)
+ return i->hardware->index;
+ }
+
+ return AVAHI_IF_UNSPEC;
+}
diff --git a/trunk/avahi-core/iface.h b/trunk/avahi-core/iface.h
new file mode 100644
index 0000000..4106ea7
--- /dev/null
+++ b/trunk/avahi-core/iface.h
@@ -0,0 +1,192 @@
+#ifndef fooifacehfoo
+#define fooifacehfoo
+
+/* $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 AvahiInterfaceMonitor AvahiInterfaceMonitor;
+typedef struct AvahiInterfaceAddress AvahiInterfaceAddress;
+typedef struct AvahiInterface AvahiInterface;
+typedef struct AvahiHwInterface AvahiHwInterface;
+
+#include <avahi-common/llist.h>
+#include <avahi-common/address.h>
+
+#include "internal.h"
+#include "cache.h"
+#include "response-sched.h"
+#include "query-sched.h"
+#include "probe-sched.h"
+#include "dns.h"
+#include "announce.h"
+#include "browse.h"
+#include "querier.h"
+
+#ifdef HAVE_NETLINK
+#include "iface-linux.h"
+#elif defined(HAVE_PF_ROUTE)
+#include "iface-pfroute.h"
+#else
+typedef struct AvahiInterfaceMonitorOSDep AvahiInterfaceMonitorOSDep;
+struct AvahiInterfaceMonitorOSDep {
+
+ unsigned query_addr_seq, query_link_seq;
+
+ enum {
+ LIST_IFACE,
+ LIST_ADDR,
+ LIST_DONE
+ } list;
+};
+#endif
+
+#define AVAHI_MAC_ADDRESS_MAX 32
+
+struct AvahiInterfaceMonitor {
+ AvahiServer *server;
+ AvahiHashmap *hashmap;
+
+ AVAHI_LLIST_HEAD(AvahiInterface, interfaces);
+ AVAHI_LLIST_HEAD(AvahiHwInterface, hw_interfaces);
+
+ int list_complete;
+ AvahiInterfaceMonitorOSDep osdep;
+};
+
+struct AvahiHwInterface {
+ AvahiInterfaceMonitor *monitor;
+
+ AVAHI_LLIST_FIELDS(AvahiHwInterface, hardware);
+
+ char *name;
+ AvahiIfIndex index;
+ int flags_ok;
+
+ unsigned mtu;
+
+ uint8_t mac_address[AVAHI_MAC_ADDRESS_MAX];
+ size_t mac_address_size;
+
+ AvahiSEntryGroup *entry_group;
+
+ AVAHI_LLIST_HEAD(AvahiInterface, interfaces);
+};
+
+struct AvahiInterface {
+ AvahiInterfaceMonitor *monitor;
+ AvahiHwInterface *hardware;
+
+ AVAHI_LLIST_FIELDS(AvahiInterface, interface);
+ AVAHI_LLIST_FIELDS(AvahiInterface, by_hardware);
+
+ AvahiProtocol protocol;
+ int announcing;
+ AvahiAddress local_mcast_address;
+ int mcast_joined;
+
+ AvahiCache *cache;
+
+ AvahiQueryScheduler *query_scheduler;
+ AvahiResponseScheduler * response_scheduler;
+ AvahiProbeScheduler *probe_scheduler;
+
+ AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses);
+ AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers);
+
+ AvahiHashmap *queriers_by_key;
+ AVAHI_LLIST_HEAD(AvahiQuerier, queriers);
+};
+
+struct AvahiInterfaceAddress {
+ AvahiInterfaceMonitor *monitor;
+ AvahiInterface *interface;
+
+ AVAHI_LLIST_FIELDS(AvahiInterfaceAddress, address);
+
+ AvahiAddress address;
+ unsigned prefix_len;
+
+ int global_scope;
+
+ AvahiSEntryGroup *entry_group;
+};
+
+AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *server);
+void avahi_interface_monitor_free(AvahiInterfaceMonitor *m);
+
+int avahi_interface_monitor_init_osdep(AvahiInterfaceMonitor *m);
+void avahi_interface_monitor_free_osdep(AvahiInterfaceMonitor *m);
+void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m);
+
+typedef void (*AvahiInterfaceMonitorWalkCallback)(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata);
+void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol, AvahiInterfaceMonitorWalkCallback callback, void* userdata);
+int avahi_dump_caches(AvahiInterfaceMonitor *m, AvahiDumpCallback callback, void* userdata);
+
+void avahi_interface_monitor_update_rrs(AvahiInterfaceMonitor *m, int remove_rrs);
+int avahi_address_is_local(AvahiInterfaceMonitor *m, const AvahiAddress *a);
+void avahi_interface_monitor_check_relevant(AvahiInterfaceMonitor *m);
+
+/* AvahiHwInterface */
+
+AvahiHwInterface *avahi_hw_interface_new(AvahiInterfaceMonitor *m, AvahiIfIndex idx);
+void avahi_hw_interface_free(AvahiHwInterface *hw, int send_goodbye);
+
+void avahi_hw_interface_update_rrs(AvahiHwInterface *hw, int remove_rrs);
+void avahi_hw_interface_check_relevant(AvahiHwInterface *hw);
+
+AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, int idx);
+
+/* AvahiInterface */
+
+AvahiInterface* avahi_interface_new(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, AvahiProtocol protocol);
+void avahi_interface_free(AvahiInterface *i, int send_goodbye);
+
+void avahi_interface_update_rrs(AvahiInterface *i, int remove_rrs);
+void avahi_interface_check_relevant(AvahiInterface *i);
+int avahi_interface_is_relevant(AvahiInterface *i);
+
+void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p);
+void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port);
+
+int avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, int immediately, unsigned *ret_id);
+int avahi_interface_withraw_query(AvahiInterface *i, unsigned id);
+int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately);
+int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, int immediately);
+
+int avahi_interface_match(AvahiInterface *i, AvahiIfIndex idx, AvahiProtocol protocol);
+int avahi_interface_address_on_link(AvahiInterface *i, const AvahiAddress *a);
+int avahi_interface_has_address(AvahiInterfaceMonitor *m, AvahiIfIndex iface, const AvahiAddress *a);
+
+AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, AvahiIfIndex idx, AvahiProtocol protocol);
+
+/* AvahiInterfaceAddress */
+
+AvahiInterfaceAddress *avahi_interface_address_new(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *addr, unsigned prefix_len);
+void avahi_interface_address_free(AvahiInterfaceAddress *a);
+
+void avahi_interface_address_update_rrs(AvahiInterfaceAddress *a, int remove_rrs);
+int avahi_interface_address_is_relevant(AvahiInterfaceAddress *a);
+
+AvahiInterfaceAddress* avahi_interface_monitor_get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr);
+
+AvahiIfIndex avahi_find_interface_for_address(AvahiInterfaceMonitor *m, const AvahiAddress *a);
+
+#endif
diff --git a/trunk/avahi-core/internal.h b/trunk/avahi-core/internal.h
new file mode 100644
index 0000000..0d88d5a
--- /dev/null
+++ b/trunk/avahi-core/internal.h
@@ -0,0 +1,227 @@
+#ifndef foointernalhfoo
+#define foointernalhfoo
+
+/* $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.
+***/
+
+/** A locally registered DNS resource record */
+typedef struct AvahiEntry AvahiEntry;
+
+#include <avahi-common/llist.h>
+#include <avahi-common/watch.h>
+
+#include "core.h"
+#include "iface.h"
+#include "prioq.h"
+#include "timeeventq.h"
+#include "announce.h"
+#include "browse.h"
+#include "dns.h"
+#include "rrlist.h"
+#include "hashmap.h"
+#include "wide-area.h"
+#include "multicast-lookup.h"
+#include "dns-srv-rr.h"
+
+#define AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX 100
+
+#define AVAHI_FLAGS_VALID(flags, max) (!((flags) & ~(max)))
+
+#define AVAHI_RR_HOLDOFF_MSEC 1000
+#define AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT 20000
+#define AVAHI_RR_RATE_LIMIT_COUNT 15
+
+typedef struct AvahiLegacyUnicastReflectSlot AvahiLegacyUnicastReflectSlot;
+
+struct AvahiLegacyUnicastReflectSlot {
+ AvahiServer *server;
+
+ uint16_t id, original_id;
+ AvahiAddress address;
+ uint16_t port;
+ int interface;
+ struct timeval elapse_time;
+ AvahiTimeEvent *time_event;
+};
+
+struct AvahiEntry {
+ AvahiServer *server;
+ AvahiSEntryGroup *group;
+
+ int dead;
+
+ AvahiPublishFlags flags;
+ AvahiRecord *record;
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+
+ AVAHI_LLIST_FIELDS(AvahiEntry, entries);
+ AVAHI_LLIST_FIELDS(AvahiEntry, by_key);
+ AVAHI_LLIST_FIELDS(AvahiEntry, by_group);
+
+ AVAHI_LLIST_HEAD(AvahiAnnouncer, announcers);
+};
+
+struct AvahiSEntryGroup {
+ AvahiServer *server;
+ int dead;
+
+ AvahiEntryGroupState state;
+ void* userdata;
+ AvahiSEntryGroupCallback callback;
+
+ unsigned n_probing;
+
+ unsigned n_register_try;
+ struct timeval register_time;
+ AvahiTimeEvent *register_time_event;
+
+ struct timeval established_at;
+
+ AVAHI_LLIST_FIELDS(AvahiSEntryGroup, groups);
+ AVAHI_LLIST_HEAD(AvahiEntry, entries);
+};
+
+struct AvahiServer {
+ const AvahiPoll *poll_api;
+
+ AvahiInterfaceMonitor *monitor;
+ AvahiServerConfig config;
+
+ AVAHI_LLIST_HEAD(AvahiEntry, entries);
+ AvahiHashmap *entries_by_key;
+
+ AVAHI_LLIST_HEAD(AvahiSEntryGroup, groups);
+
+ AVAHI_LLIST_HEAD(AvahiSRecordBrowser, record_browsers);
+ AvahiHashmap *record_browser_hashmap;
+ AVAHI_LLIST_HEAD(AvahiSHostNameResolver, host_name_resolvers);
+ AVAHI_LLIST_HEAD(AvahiSAddressResolver, address_resolvers);
+ AVAHI_LLIST_HEAD(AvahiSDomainBrowser, domain_browsers);
+ AVAHI_LLIST_HEAD(AvahiSServiceTypeBrowser, service_type_browsers);
+ AVAHI_LLIST_HEAD(AvahiSServiceBrowser, service_browsers);
+ AVAHI_LLIST_HEAD(AvahiSServiceResolver, service_resolvers);
+ AVAHI_LLIST_HEAD(AvahiSDNSServerBrowser, dns_server_browsers);
+
+ int need_entry_cleanup, need_group_cleanup, need_browser_cleanup;
+
+ AvahiTimeEventQueue *time_event_queue;
+
+ char *host_name, *host_name_fqdn, *domain_name;
+
+ int fd_ipv4, fd_ipv6,
+ /* The following two sockets two are used for reflection only */
+ fd_legacy_unicast_ipv4, fd_legacy_unicast_ipv6;
+
+ AvahiWatch *watch_ipv4, *watch_ipv6,
+ *watch_legacy_unicast_ipv4, *watch_legacy_unicast_ipv6;
+
+ AvahiServerState state;
+ AvahiServerCallback callback;
+ void* userdata;
+
+ AvahiSEntryGroup *hinfo_entry_group;
+ AvahiSEntryGroup *browse_domain_entry_group;
+ unsigned n_host_rr_pending;
+
+ /* Used for assembling responses */
+ AvahiRecordList *record_list;
+
+ /* Used for reflection of legacy unicast packets */
+ 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;
+
+ AvahiStringList *static_browse_domains;
+};
+
+void avahi_entry_free(AvahiServer*s, AvahiEntry *e);
+void avahi_entry_group_free(AvahiServer *s, AvahiSEntryGroup *g);
+
+void avahi_cleanup_dead_entries(AvahiServer *s);
+
+void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary);
+void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response);
+void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int is_probe);
+
+void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state);
+
+int avahi_entry_is_commited(AvahiEntry *e);
+
+void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata);
+
+void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, void *userdata);
+
+void avahi_server_decrease_host_rr_pending(AvahiServer *s);
+
+int avahi_server_set_errno(AvahiServer *s, int error);
+
+int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name);
+int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record);
+
+int avahi_server_add_ptr(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ uint32_t ttl,
+ const char *name,
+ const char *dest);
+
+#define AVAHI_CHECK_VALIDITY(server, expression, error) { \
+ if (!(expression)) \
+ return avahi_server_set_errno((server), (error)); \
+}
+
+#define AVAHI_CHECK_VALIDITY_RETURN_NULL(server, expression, error) { \
+ if (!(expression)) { \
+ avahi_server_set_errno((server), (error)); \
+ return NULL; \
+ } \
+}
+
+#define AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(server, expression, error) {\
+ if (!(expression)) { \
+ ret = avahi_server_set_errno((server), (error)); \
+ goto fail; \
+ } \
+}
+
+#define AVAHI_ASSERT_TRUE(expression) { \
+ int __tmp = !!(expression); \
+ assert(__tmp); \
+}
+
+#define AVAHI_ASSERT_SUCCESS(expression) { \
+ int __tmp = (expression); \
+ assert(__tmp == 0); \
+}
+
+#endif
diff --git a/trunk/avahi-core/log.c b/trunk/avahi-core/log.c
new file mode 100644
index 0000000..bfd4021
--- /dev/null
+++ b/trunk/avahi-core/log.c
@@ -0,0 +1,88 @@
+/* $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 <stdio.h>
+#include <stdarg.h>
+
+#include "log.h"
+
+static AvahiLogFunction log_function = NULL;
+
+void avahi_set_log_function(AvahiLogFunction function) {
+ log_function = function;
+}
+
+void avahi_log_ap(AvahiLogLevel level, const char*format, va_list ap) {
+ char txt[256];
+
+ vsnprintf(txt, sizeof(txt), format, ap);
+
+ if (log_function)
+ log_function(level, txt);
+ else
+ fprintf(stderr, "%s\n", txt);
+}
+
+void avahi_log(AvahiLogLevel level, const char*format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ avahi_log_ap(level, format, ap);
+ va_end(ap);
+}
+
+void avahi_log_error(const char*format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ avahi_log_ap(AVAHI_LOG_ERROR, format, ap);
+ va_end(ap);
+}
+
+void avahi_log_warn(const char*format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ avahi_log_ap(AVAHI_LOG_WARN, format, ap);
+ va_end(ap);
+}
+
+void avahi_log_notice(const char*format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ avahi_log_ap(AVAHI_LOG_NOTICE, format, ap);
+ va_end(ap);
+}
+
+void avahi_log_info(const char*format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ avahi_log_ap(AVAHI_LOG_INFO, format, ap);
+ va_end(ap);
+}
+
+void avahi_log_debug(const char*format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ avahi_log_ap(AVAHI_LOG_DEBUG, format, ap);
+ va_end(ap);
+}
diff --git a/trunk/avahi-core/log.h b/trunk/avahi-core/log.h
new file mode 100644
index 0000000..25e3940
--- /dev/null
+++ b/trunk/avahi-core/log.h
@@ -0,0 +1,75 @@
+#ifndef foologhfoo
+#define foologhfoo
+
+/* $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 <stdarg.h>
+
+#include <avahi-common/cdecl.h>
+#include <avahi-common/gccmacro.h>
+
+/** \file log.h Extensible logging subsystem */
+
+AVAHI_C_DECL_BEGIN
+
+/** Log level for avahi_log_xxx() */
+typedef enum {
+ AVAHI_LOG_ERROR = 0, /**< Error messages */
+ AVAHI_LOG_WARN = 1, /**< Warning messages */
+ AVAHI_LOG_NOTICE = 2, /**< Notice messages */
+ AVAHI_LOG_INFO = 3, /**< Info messages */
+ AVAHI_LOG_DEBUG = 4, /**< Debug messages */
+ AVAHI_LOG_LEVEL_MAX
+} AvahiLogLevel;
+
+/** Prototype for a user supplied log function */
+typedef void (*AvahiLogFunction)(AvahiLogLevel level, const char *txt);
+
+/** Set a user supplied log function, replacing the default which
+ * prints to log messages unconditionally to STDERR. Pass NULL for
+ * resetting to the default log function */
+void avahi_set_log_function(AvahiLogFunction function);
+
+/** Issue a log message using a va_list object */
+void avahi_log_ap(AvahiLogLevel level, const char *format, va_list ap);
+
+/** Issue a log message by passing a log level and a format string */
+void avahi_log(AvahiLogLevel level, const char*format, ...) AVAHI_GCC_PRINTF_ATTR23;
+
+/** Shortcut for avahi_log(AVAHI_LOG_ERROR, ...) */
+void avahi_log_error(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
+
+/** Shortcut for avahi_log(AVAHI_LOG_WARN, ...) */
+void avahi_log_warn(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
+
+/** Shortcut for avahi_log(AVAHI_LOG_NOTICE, ...) */
+void avahi_log_notice(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
+
+/** Shortcut for avahi_log(AVAHI_LOG_INFO, ...) */
+void avahi_log_info(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
+
+/** Shortcut for avahi_log(AVAHI_LOG_DEBUG, ...) */
+void avahi_log_debug(const char*format, ...) AVAHI_GCC_PRINTF_ATTR12;
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/lookup.h b/trunk/avahi-core/lookup.h
new file mode 100644
index 0000000..0ce6fe8
--- /dev/null
+++ b/trunk/avahi-core/lookup.h
@@ -0,0 +1,237 @@
+#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 avahi-core/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. */
+
+/** 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;
+
+#include <avahi-common/cdecl.h>
+#include <avahi-common/defs.h>
+#include <avahi-core/core.h>
+
+AVAHI_C_DECL_BEGIN
+
+/** 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);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/multicast-lookup.c b/trunk/avahi-core/multicast-lookup.c
new file mode 100644
index 0000000..c3afcb0
--- /dev/null
+++ b/trunk/avahi-core/multicast-lookup.c
@@ -0,0 +1,352 @@
+/* $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 <avahi-common/malloc.h>
+#include <avahi-common/timeval.h>
+
+#include "internal.h"
+#include "browse.h"
+#include "socket.h"
+#include "log.h"
+#include "hashmap.h"
+#include "multicast-lookup.h"
+#include "rr-util.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_RESULT_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 tv;
+
+ 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, &tv);
+ l->queriers_added = 1;
+
+ /* Add a second */
+ avahi_timeval_add(&tv, 1000000);
+
+ /* Issue the ALL_FOR_NOW event one second after the querier was initially created */
+ l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &tv, 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_RESULT_CACHED|AVAHI_LOOKUP_RESULT_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_RESULT_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_RESULT_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/trunk/avahi-core/multicast-lookup.h b/trunk/avahi-core/multicast-lookup.h
new file mode 100644
index 0000000..43e240d
--- /dev/null
+++ b/trunk/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/trunk/avahi-core/netlink.c b/trunk/avahi-core/netlink.c
new file mode 100644
index 0000000..7411c90
--- /dev/null
+++ b/trunk/avahi-core/netlink.c
@@ -0,0 +1,211 @@
+/* $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 <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <assert.h>
+
+#include <avahi-common/malloc.h>
+#include "netlink.h"
+#include "log.h"
+
+struct AvahiNetlink {
+ int fd;
+ unsigned seq;
+ AvahiNetlinkCallback callback;
+ void* userdata;
+ uint8_t* buffer;
+ size_t buffer_length;
+
+ const AvahiPoll *poll_api;
+ AvahiWatch *watch;
+};
+
+int avahi_netlink_work(AvahiNetlink *nl, int block) {
+ ssize_t bytes;
+ struct msghdr smsg;
+ struct cmsghdr *cmsg;
+ struct ucred *cred;
+ struct iovec iov;
+ struct nlmsghdr *p;
+ char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
+
+ assert(nl);
+
+ iov.iov_base = nl->buffer;
+ iov.iov_len = nl->buffer_length;
+
+ smsg.msg_name = (void*) NULL;
+ smsg.msg_namelen = 0;
+ smsg.msg_iov = &iov;
+ smsg.msg_iovlen = 1;
+ smsg.msg_control = cred_msg;
+ smsg.msg_controllen = sizeof(cred_msg);
+ smsg.msg_flags = (block ? 0 : MSG_DONTWAIT);
+
+ if ((bytes = recvmsg(nl->fd, &smsg, 0)) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+
+ avahi_log_error(__FILE__": recvmsg() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ cmsg = CMSG_FIRSTHDR(&smsg);
+ cred = (struct ucred *) CMSG_DATA (cmsg);
+
+ if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
+ avahi_log_error("No sender credentials received, ignoring data.");
+ return -1;
+ }
+
+ if (cred->uid != 0) {
+ avahi_log_warn("Netlink message received from cred->uid != 0 (%d)", cred->uid);
+ return -1;
+ }
+
+ p = (struct nlmsghdr *) nl->buffer;
+
+ assert(nl->callback);
+
+ for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
+ if (!NLMSG_OK(p, (size_t) bytes)) {
+ avahi_log_warn(__FILE__": packet truncated");
+ return -1;
+ }
+
+ nl->callback(nl, p, nl->userdata);
+ }
+
+ return 0;
+}
+
+static void socket_event(AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent event, void *userdata) {
+ AvahiNetlink *nl = userdata;
+
+ assert(w);
+ assert(nl);
+ assert(fd == nl->fd);
+
+ avahi_netlink_work(nl, 0);
+}
+
+AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, void (*cb) (AvahiNetlink *nl, struct nlmsghdr *n, void* userdata), void* userdata) {
+ int fd = -1;
+ const int on = 1;
+ struct sockaddr_nl addr;
+ AvahiNetlink *nl = NULL;
+
+ assert(poll_api);
+ assert(cb);
+
+ if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
+ avahi_log_error(__FILE__": socket(PF_NETLINK): %s", strerror(errno));
+ return NULL;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = groups;
+ addr.nl_pid = getpid();
+
+ if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ avahi_log_error(__FILE__": bind(): %s", strerror(errno));
+ goto fail;
+ }
+
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
+ avahi_log_error(__FILE__": bind(): %s", strerror(errno));
+ goto fail;
+ }
+
+ if (!(nl = avahi_new(AvahiNetlink, 1))) {
+ avahi_log_error(__FILE__": avahi_new() failed.");
+ goto fail;
+ }
+
+ nl->poll_api = poll_api;
+ nl->fd = fd;
+ nl->seq = 0;
+ nl->callback = cb;
+ nl->userdata = userdata;
+
+ if (!(nl->buffer = avahi_new(uint8_t, nl->buffer_length = 64*1024))) {
+ avahi_log_error(__FILE__": avahi_new() failed.");
+ goto fail;
+ }
+
+ if (!(nl->watch = poll_api->watch_new(poll_api, fd, AVAHI_WATCH_IN, socket_event, nl))) {
+ avahi_log_error(__FILE__": Failed to create watch.");
+ goto fail;
+ }
+
+ return nl;
+
+fail:
+
+ if (fd >= 0)
+ close(fd);
+
+ if (nl) {
+ avahi_free(nl->buffer);
+ avahi_free(nl);
+ }
+
+ return NULL;
+}
+
+void avahi_netlink_free(AvahiNetlink *nl) {
+ assert(nl);
+
+ if (nl->watch)
+ nl->poll_api->watch_free(nl->watch);
+
+ if (nl->fd >= 0)
+ close(nl->fd);
+
+ avahi_free(nl->buffer);
+ avahi_free(nl);
+}
+
+int avahi_netlink_send(AvahiNetlink *nl, struct nlmsghdr *m, unsigned *ret_seq) {
+ assert(nl);
+ assert(m);
+
+ m->nlmsg_seq = nl->seq++;
+ m->nlmsg_flags |= NLM_F_ACK;
+
+ if (send(nl->fd, m, m->nlmsg_len, 0) < 0) {
+ avahi_log_error(__FILE__": send(): %s", strerror(errno));
+ return -1;
+ }
+
+ if (ret_seq)
+ *ret_seq = m->nlmsg_seq;
+
+ return 0;
+}
diff --git a/trunk/avahi-core/netlink.h b/trunk/avahi-core/netlink.h
new file mode 100644
index 0000000..8f2f8cb
--- /dev/null
+++ b/trunk/avahi-core/netlink.h
@@ -0,0 +1,43 @@
+#ifndef foonetlinkhfoo
+#define foonetlinkhfoo
+
+/* $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 <sys/socket.h>
+#include <asm/types.h>
+#include <inttypes.h>
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <avahi-common/watch.h>
+
+typedef struct AvahiNetlink AvahiNetlink;
+
+typedef void (*AvahiNetlinkCallback)(AvahiNetlink *n, struct nlmsghdr *m, void* userdata);
+
+AvahiNetlink *avahi_netlink_new(const AvahiPoll *poll_api, uint32_t groups, AvahiNetlinkCallback callback, void* userdata);
+void avahi_netlink_free(AvahiNetlink *n);
+int avahi_netlink_send(AvahiNetlink *n, struct nlmsghdr *m, unsigned *ret_seq);
+int avahi_netlink_work(AvahiNetlink *n, int block);
+
+#endif
diff --git a/trunk/avahi-core/prioq-test.c b/trunk/avahi-core/prioq-test.c
new file mode 100644
index 0000000..d85a222
--- /dev/null
+++ b/trunk/avahi-core/prioq-test.c
@@ -0,0 +1,122 @@
+/* $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 <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <avahi-common/gccmacro.h>
+
+#include "prioq.h"
+
+#define POINTER_TO_INT(p) ((int) (p))
+#define INT_TO_POINTER(i) ((void*) (i))
+
+static int compare_int(const void* a, const void* b) {
+ int i = POINTER_TO_INT(a), j = POINTER_TO_INT(b);
+
+ return i < j ? -1 : (i > j ? 1 : 0);
+}
+
+static int compare_ptr(const void* a, const void* b) {
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+static void rec(AvahiPrioQueueNode *n) {
+ if (!n)
+ return;
+
+ if (n->left)
+ assert(n->left->parent == n);
+
+ if (n->right)
+ assert(n->right->parent == n);
+
+ if (n->parent) {
+ assert(n->parent->left == n || n->parent->right == n);
+
+ if (n->parent->left == n)
+ assert(n->next == n->parent->right);
+ }
+
+ if (!n->next) {
+ assert(n->queue->last == n);
+
+ if (n->parent && n->parent->left == n)
+ assert(n->parent->right == NULL);
+ }
+
+
+ if (n->parent) {
+ int a = POINTER_TO_INT(n->parent->data), b = POINTER_TO_INT(n->data);
+ if (a > b) {
+ printf("%i <= %i: NO\n", a, b);
+ abort();
+ }
+ }
+
+ rec(n->left);
+ rec(n->right);
+}
+
+int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) {
+ AvahiPrioQueue *q, *q2;
+ int i;
+
+ q = avahi_prio_queue_new(compare_int);
+ q2 = avahi_prio_queue_new(compare_ptr);
+
+ srand(time(NULL));
+
+ for (i = 0; i < 10000; i++)
+ avahi_prio_queue_put(q2, avahi_prio_queue_put(q, INT_TO_POINTER(random() & 0xFFFF)));
+
+ while (q2->root) {
+ rec(q->root);
+ rec(q2->root);
+
+ assert(q->n_nodes == q2->n_nodes);
+
+ printf("%i\n", POINTER_TO_INT(((AvahiPrioQueueNode*)q2->root->data)->data));
+
+ avahi_prio_queue_remove(q, q2->root->data);
+ avahi_prio_queue_remove(q2, q2->root);
+ }
+
+
+/* prev = 0; */
+/* while (q->root) { */
+/* int v = GPOINTER_TO_INT(q->root->data); */
+/* rec(q->root); */
+/* printf("%i\n", v); */
+/* avahi_prio_queue_remove(q, q->root); */
+/* assert(v >= prev); */
+/* prev = v; */
+/* } */
+
+ avahi_prio_queue_free(q);
+ return 0;
+}
diff --git a/trunk/avahi-core/prioq.c b/trunk/avahi-core/prioq.c
new file mode 100644
index 0000000..8d91d87
--- /dev/null
+++ b/trunk/avahi-core/prioq.c
@@ -0,0 +1,390 @@
+/* $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 <assert.h>
+#include <stdlib.h>
+
+#include <avahi-common/malloc.h>
+
+#include "prioq.h"
+
+AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare) {
+ AvahiPrioQueue *q;
+ assert(compare);
+
+ if (!(q = avahi_new(AvahiPrioQueue, 1)))
+ return NULL; /* OOM */
+
+ q->root = q->last = NULL;
+ q->n_nodes = 0;
+ q->compare = compare;
+
+ return q;
+}
+
+void avahi_prio_queue_free(AvahiPrioQueue *q) {
+ assert(q);
+
+ while (q->last)
+ avahi_prio_queue_remove(q, q->last);
+
+ assert(!q->n_nodes);
+ avahi_free(q);
+}
+
+static AvahiPrioQueueNode* get_node_at_xy(AvahiPrioQueue *q, unsigned x, unsigned y) {
+ unsigned r;
+ AvahiPrioQueueNode *n;
+ assert(q);
+
+ n = q->root;
+ assert(n);
+
+ for (r = 0; r < y; r++) {
+ assert(n);
+
+ if ((x >> (y-r-1)) & 1)
+ n = n->right;
+ else
+ n = n->left;
+ }
+
+ assert(n->x == x);
+ assert(n->y == y);
+
+ return n;
+}
+
+static void exchange_nodes(AvahiPrioQueue *q, AvahiPrioQueueNode *a, AvahiPrioQueueNode *b) {
+ AvahiPrioQueueNode *l, *r, *p, *ap, *an, *bp, *bn;
+ unsigned t;
+ assert(q);
+ assert(a);
+ assert(b);
+ assert(a != b);
+
+ /* Swap positions */
+ t = a->x; a->x = b->x; b->x = t;
+ t = a->y; a->y = b->y; b->y = t;
+
+ if (a->parent == b) {
+ /* B is parent of A */
+
+ p = b->parent;
+ b->parent = a;
+
+ if ((a->parent = p)) {
+ if (a->parent->left == b)
+ a->parent->left = a;
+ else
+ a->parent->right = a;
+ } else
+ q->root = a;
+
+ if (b->left == a) {
+ if ((b->left = a->left))
+ b->left->parent = b;
+ a->left = b;
+
+ r = a->right;
+ if ((a->right = b->right))
+ a->right->parent = a;
+ if ((b->right = r))
+ b->right->parent = b;
+
+ } else {
+ if ((b->right = a->right))
+ b->right->parent = b;
+ a->right = b;
+
+ l = a->left;
+ if ((a->left = b->left))
+ a->left->parent = a;
+ if ((b->left = l))
+ b->left->parent = b;
+ }
+ } else if (b->parent == a) {
+ /* A ist parent of B */
+
+ p = a->parent;
+ a->parent = b;
+
+ if ((b->parent = p)) {
+ if (b->parent->left == a)
+ b->parent->left = b;
+ else
+ b->parent->right = b;
+ } else
+ q->root = b;
+
+ if (a->left == b) {
+ if ((a->left = b->left))
+ a->left->parent = a;
+ b->left = a;
+
+ r = a->right;
+ if ((a->right = b->right))
+ a->right->parent = a;
+ if ((b->right = r))
+ b->right->parent = b;
+ } else {
+ if ((a->right = b->right))
+ a->right->parent = a;
+ b->right = a;
+
+ l = a->left;
+ if ((a->left = b->left))
+ a->left->parent = a;
+ if ((b->left = l))
+ b->left->parent = b;
+ }
+ } else {
+ AvahiPrioQueueNode *apl = NULL, *bpl = NULL;
+
+ /* Swap parents */
+ ap = a->parent;
+ bp = b->parent;
+
+ if (ap)
+ apl = ap->left;
+ if (bp)
+ bpl = bp->left;
+
+ if ((a->parent = bp)) {
+ if (bpl == b)
+ bp->left = a;
+ else
+ bp->right = a;
+ } else
+ q->root = a;
+
+ if ((b->parent = ap)) {
+ if (apl == a)
+ ap->left = b;
+ else
+ ap->right = b;
+ } else
+ q->root = b;
+
+ /* Swap children */
+ l = a->left;
+ r = a->right;
+
+ if ((a->left = b->left))
+ a->left->parent = a;
+
+ if ((b->left = l))
+ b->left->parent = b;
+
+ if ((a->right = b->right))
+ a->right->parent = a;
+
+ if ((b->right = r))
+ b->right->parent = b;
+ }
+
+ /* Swap siblings */
+ ap = a->prev; an = a->next;
+ bp = b->prev; bn = b->next;
+
+ if (a->next == b) {
+ /* A is predecessor of B */
+ a->prev = b;
+ b->next = a;
+
+ if ((a->next = bn))
+ a->next->prev = a;
+ else
+ q->last = a;
+
+ if ((b->prev = ap))
+ b->prev->next = b;
+
+ } else if (b->next == a) {
+ /* B is predecessor of A */
+ a->next = b;
+ b->prev = a;
+
+ if ((a->prev = bp))
+ a->prev->next = a;
+
+ if ((b->next = an))
+ b->next->prev = b;
+ else
+ q->last = b;
+
+ } else {
+ /* A is no neighbour of B */
+
+ if ((a->prev = bp))
+ a->prev->next = a;
+
+ if ((a->next = bn))
+ a->next->prev = a;
+ else
+ q->last = a;
+
+ if ((b->prev = ap))
+ b->prev->next = b;
+
+ if ((b->next = an))
+ b->next->prev = b;
+ else
+ q->last = b;
+ }
+}
+
+/* Move a node to the correct position */
+void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n) {
+ assert(q);
+ assert(n);
+ assert(n->queue == q);
+
+ /* Move up until the position is OK */
+ while (n->parent && q->compare(n->parent->data, n->data) > 0)
+ exchange_nodes(q, n, n->parent);
+
+ /* Move down until the position is OK */
+ for (;;) {
+ AvahiPrioQueueNode *min;
+
+ if (!(min = n->left)) {
+ /* No children */
+ assert(!n->right);
+ break;
+ }
+
+ if (n->right && q->compare(n->right->data, min->data) < 0)
+ min = n->right;
+
+ /* min now contains the smaller one of our two children */
+
+ if (q->compare(n->data, min->data) <= 0)
+ /* Order OK */
+ break;
+
+ exchange_nodes(q, n, min);
+ }
+}
+
+AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data) {
+ AvahiPrioQueueNode *n;
+ assert(q);
+
+ if (!(n = avahi_new(AvahiPrioQueueNode, 1)))
+ return NULL; /* OOM */
+
+ n->queue = q;
+ n->data = data;
+
+ if (q->last) {
+ assert(q->root);
+ assert(q->n_nodes);
+
+ n->y = q->last->y;
+ n->x = q->last->x+1;
+
+ if (n->x >= ((unsigned) 1 << n->y)) {
+ n->x = 0;
+ n->y++;
+ }
+
+ q->last->next = n;
+ n->prev = q->last;
+
+ assert(n->y > 0);
+ n->parent = get_node_at_xy(q, n->x/2, n->y-1);
+
+ if (n->x & 1)
+ n->parent->right = n;
+ else
+ n->parent->left = n;
+ } else {
+ assert(!q->root);
+ assert(!q->n_nodes);
+
+ n->y = n->x = 0;
+ q->root = n;
+ n->prev = n->parent = NULL;
+ }
+
+ n->next = n->left = n->right = NULL;
+ q->last = n;
+ q->n_nodes++;
+
+ avahi_prio_queue_shuffle(q, n);
+
+ return n;
+}
+
+void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n) {
+ assert(q);
+ assert(n);
+ assert(q == n->queue);
+
+ if (n != q->last) {
+ AvahiPrioQueueNode *replacement = q->last;
+ exchange_nodes(q, replacement, n);
+ avahi_prio_queue_remove(q, n);
+ avahi_prio_queue_shuffle(q, replacement);
+ return;
+ }
+
+ assert(n == q->last);
+ assert(!n->next);
+ assert(!n->left);
+ assert(!n->right);
+
+ q->last = n->prev;
+
+ if (n->prev) {
+ n->prev->next = NULL;
+ assert(n->parent);
+ } else
+ assert(!n->parent);
+
+ if (n->parent) {
+ assert(n->prev);
+ if (n->parent->left == n) {
+ assert(n->parent->right == NULL);
+ n->parent->left = NULL;
+ } else {
+ assert(n->parent->right == n);
+ assert(n->parent->left != NULL);
+ n->parent->right = NULL;
+ }
+ } else {
+ assert(q->root == n);
+ assert(!n->prev);
+ assert(q->n_nodes == 1);
+ q->root = NULL;
+ }
+
+ avahi_free(n);
+
+ assert(q->n_nodes > 0);
+ q->n_nodes--;
+}
+
diff --git a/trunk/avahi-core/prioq.h b/trunk/avahi-core/prioq.h
new file mode 100644
index 0000000..ace92f6
--- /dev/null
+++ b/trunk/avahi-core/prioq.h
@@ -0,0 +1,51 @@
+#ifndef fooprioqhfoo
+#define fooprioqhfoo
+
+/* $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 AvahiPrioQueue AvahiPrioQueue;
+typedef struct AvahiPrioQueueNode AvahiPrioQueueNode;
+
+typedef int (*AvahiPQCompareFunc)(const void* a, const void* b);
+
+struct AvahiPrioQueue {
+ AvahiPrioQueueNode *root, *last;
+ unsigned n_nodes;
+ AvahiPQCompareFunc compare;
+};
+
+struct AvahiPrioQueueNode {
+ AvahiPrioQueue *queue;
+ void* data;
+ unsigned x, y;
+ AvahiPrioQueueNode *left, *right, *parent, *next, *prev;
+};
+
+AvahiPrioQueue* avahi_prio_queue_new(AvahiPQCompareFunc compare);
+void avahi_prio_queue_free(AvahiPrioQueue *q);
+
+AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, void* data);
+void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n);
+
+void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n);
+
+#endif
diff --git a/trunk/avahi-core/probe-sched.c b/trunk/avahi-core/probe-sched.c
new file mode 100644
index 0000000..f430bce
--- /dev/null
+++ b/trunk/avahi-core/probe-sched.c
@@ -0,0 +1,402 @@
+/* $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 <avahi-common/domain.h>
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+
+#include "probe-sched.h"
+#include "log.h"
+#include "rr-util.h"
+
+#define AVAHI_PROBE_HISTORY_MSEC 150
+#define AVAHI_PROBE_DEFER_MSEC 50
+
+typedef struct AvahiProbeJob AvahiProbeJob;
+
+struct AvahiProbeJob {
+ AvahiProbeScheduler *scheduler;
+ AvahiTimeEvent *time_event;
+
+ int chosen; /* Use for packet assembling */
+ int done;
+ struct timeval delivery;
+
+ AvahiRecord *record;
+
+ AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs);
+};
+
+struct AvahiProbeScheduler {
+ AvahiInterface *interface;
+ AvahiTimeEventQueue *time_event_queue;
+
+ AVAHI_LLIST_HEAD(AvahiProbeJob, jobs);
+ AVAHI_LLIST_HEAD(AvahiProbeJob, history);
+};
+
+static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record, int done) {
+ AvahiProbeJob *pj;
+
+ assert(s);
+ assert(record);
+
+ if (!(pj = avahi_new(AvahiProbeJob, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ return NULL; /* OOM */
+ }
+
+ pj->scheduler = s;
+ pj->record = avahi_record_ref(record);
+ pj->time_event = NULL;
+ pj->chosen = 0;
+
+ if ((pj->done = done))
+ AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj);
+ else
+ AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj);
+
+ return pj;
+}
+
+static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) {
+ assert(pj);
+
+ if (pj->time_event)
+ avahi_time_event_free(pj->time_event);
+
+ if (pj->done)
+ AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->history, pj);
+ else
+ AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj);
+
+ avahi_record_unref(pj->record);
+ avahi_free(pj);
+}
+
+static void elapse_callback(AvahiTimeEvent *e, void* data);
+
+static void job_set_elapse_time(AvahiProbeScheduler *s, AvahiProbeJob *pj, unsigned msec, unsigned jitter) {
+ struct timeval tv;
+
+ assert(s);
+ assert(pj);
+
+ avahi_elapse_time(&tv, msec, jitter);
+
+ if (pj->time_event)
+ avahi_time_event_update(pj->time_event, &tv);
+ else
+ pj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, pj);
+}
+
+static void job_mark_done(AvahiProbeScheduler *s, AvahiProbeJob *pj) {
+ assert(s);
+ assert(pj);
+
+ assert(!pj->done);
+
+ AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj);
+ AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj);
+
+ pj->done = 1;
+
+ job_set_elapse_time(s, pj, AVAHI_PROBE_HISTORY_MSEC, 0);
+ gettimeofday(&pj->delivery, NULL);
+}
+
+AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i) {
+ AvahiProbeScheduler *s;
+
+ assert(i);
+
+ if (!(s = avahi_new(AvahiProbeScheduler, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ return NULL;
+ }
+
+ s->interface = i;
+ s->time_event_queue = i->monitor->server->time_event_queue;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->jobs);
+ AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->history);
+
+ return s;
+}
+
+void avahi_probe_scheduler_free(AvahiProbeScheduler *s) {
+ assert(s);
+
+ avahi_probe_scheduler_clear(s);
+ avahi_free(s);
+}
+
+void avahi_probe_scheduler_clear(AvahiProbeScheduler *s) {
+ assert(s);
+
+ while (s->jobs)
+ job_free(s, s->jobs);
+ while (s->history)
+ job_free(s, s->history);
+}
+
+static int packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) {
+ size_t size;
+ AvahiKey *k;
+ int b;
+
+ assert(s);
+ assert(p);
+ assert(pj);
+
+ assert(!pj->chosen);
+
+ /* Estimate the size for this record */
+ size =
+ avahi_key_get_estimate_size(pj->record->key) +
+ avahi_record_get_estimate_size(pj->record);
+
+ /* Too large */
+ if (size > avahi_dns_packet_space(p))
+ return 0;
+
+ /* Create the probe query */
+ if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY)))
+ return 0; /* OOM */
+
+ b = !!avahi_dns_packet_append_key(p, k, 0);
+ assert(b);
+
+ /* Mark this job for addition to the packet */
+ pj->chosen = 1;
+
+ /* Scan for more jobs whith matching key pattern */
+ for (pj = s->jobs; pj; pj = pj->jobs_next) {
+ if (pj->chosen)
+ continue;
+
+ /* Does the record match the probe? */
+ if (k->clazz != pj->record->key->clazz || !avahi_domain_equal(k->name, pj->record->key->name))
+ continue;
+
+ /* This job wouldn't fit in */
+ if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p))
+ break;
+
+ /* Mark this job for addition to the packet */
+ pj->chosen = 1;
+ }
+
+ avahi_key_unref(k);
+
+ return 1;
+}
+
+static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) {
+ AvahiProbeJob *pj = data, *next;
+ AvahiProbeScheduler *s;
+ AvahiDnsPacket *p;
+ unsigned n;
+
+ assert(pj);
+ s = pj->scheduler;
+
+ if (pj->done) {
+ /* Lets remove it from the history */
+ job_free(s, pj);
+ return;
+ }
+
+ if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu)))
+ return; /* OOM */
+ n = 1;
+
+ /* Add the import probe */
+ if (!packet_add_probe_query(s, p, pj)) {
+ size_t size;
+ AvahiKey *k;
+ int b;
+
+ avahi_dns_packet_free(p);
+
+ /* The probe didn't fit in the package, so let's allocate a larger one */
+
+ size =
+ avahi_key_get_estimate_size(pj->record->key) +
+ avahi_record_get_estimate_size(pj->record) +
+ AVAHI_DNS_PACKET_HEADER_SIZE;
+
+ if (size > AVAHI_DNS_PACKET_SIZE_MAX)
+ size = AVAHI_DNS_PACKET_SIZE_MAX;
+
+ if (!(p = avahi_dns_packet_new_query(size)))
+ return; /* OOM */
+
+ if (!(k = avahi_key_new(pj->record->key->name, pj->record->key->clazz, AVAHI_DNS_TYPE_ANY))) {
+ avahi_dns_packet_free(p);
+ return; /* OOM */
+ }
+
+ b = avahi_dns_packet_append_key(p, k, 0) && avahi_dns_packet_append_record(p, pj->record, 0, 0);
+ avahi_key_unref(k);
+
+ if (b) {
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1);
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1);
+ avahi_interface_send_packet(s->interface, p);
+ } else
+ avahi_log_warn("Probe record too large, cannot send");
+
+ avahi_dns_packet_free(p);
+ job_mark_done(s, pj);
+
+ return;
+ }
+
+ /* Try to fill up packet with more probes, if available */
+ for (pj = s->jobs; pj; pj = pj->jobs_next) {
+
+ if (pj->chosen)
+ continue;
+
+ if (!packet_add_probe_query(s, p, pj))
+ break;
+
+ n++;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
+
+ n = 0;
+
+ /* Now add the chosen records to the authorative section */
+ for (pj = s->jobs; pj; pj = next) {
+
+ next = pj->jobs_next;
+
+ if (!pj->chosen)
+ continue;
+
+ if (!avahi_dns_packet_append_record(p, pj->record, 0, 0)) {
+/* avahi_log_warn("Bad probe size estimate!"); */
+
+ /* Unmark all following jobs */
+ for (; pj; pj = pj->jobs_next)
+ pj->chosen = 0;
+
+ break;
+ }
+
+ job_mark_done(s, pj);
+
+ n ++;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n);
+
+ /* Send it now */
+ avahi_interface_send_packet(s->interface, p);
+ avahi_dns_packet_free(p);
+}
+
+static AvahiProbeJob* find_scheduled_job(AvahiProbeScheduler *s, AvahiRecord *record) {
+ AvahiProbeJob *pj;
+
+ assert(s);
+ assert(record);
+
+ for (pj = s->jobs; pj; pj = pj->jobs_next) {
+ assert(!pj->done);
+
+ if (avahi_record_equal_no_ttl(pj->record, record))
+ return pj;
+ }
+
+ return NULL;
+}
+
+static AvahiProbeJob* find_history_job(AvahiProbeScheduler *s, AvahiRecord *record) {
+ AvahiProbeJob *pj;
+
+ assert(s);
+ assert(record);
+
+ for (pj = s->history; pj; pj = pj->jobs_next) {
+ assert(pj->done);
+
+ if (avahi_record_equal_no_ttl(pj->record, record)) {
+ /* Check whether this entry is outdated */
+
+ if (avahi_age(&pj->delivery) > AVAHI_PROBE_HISTORY_MSEC*1000) {
+ /* it is outdated, so let's remove it */
+ job_free(s, pj);
+ return NULL;
+ }
+
+ return pj;
+ }
+ }
+
+ return NULL;
+}
+
+int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately) {
+ AvahiProbeJob *pj;
+ struct timeval tv;
+
+ assert(s);
+ assert(record);
+ assert(!avahi_key_is_pattern(record->key));
+
+ if ((pj = find_history_job(s, record)))
+ return 0;
+
+ avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0);
+
+ if ((pj = find_scheduled_job(s, record))) {
+
+ if (avahi_timeval_compare(&tv, &pj->delivery) < 0) {
+ /* If the new entry should be scheduled earlier, update the old entry */
+ pj->delivery = tv;
+ avahi_time_event_update(pj->time_event, &pj->delivery);
+ }
+
+ return 1;
+ } else {
+ /* Create a new job and schedule it */
+ if (!(pj = job_new(s, record, 0)))
+ return 0; /* OOM */
+
+ pj->delivery = tv;
+ pj->time_event = avahi_time_event_new(s->time_event_queue, &pj->delivery, elapse_callback, pj);
+
+
+/* avahi_log_debug("Accepted new probe job."); */
+
+ return 1;
+ }
+}
diff --git a/trunk/avahi-core/probe-sched.h b/trunk/avahi-core/probe-sched.h
new file mode 100644
index 0000000..3af1319
--- /dev/null
+++ b/trunk/avahi-core/probe-sched.h
@@ -0,0 +1,36 @@
+#ifndef fooprobeschedhfoo
+#define fooprobeschedhfoo
+
+/* $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 AvahiProbeScheduler AvahiProbeScheduler;
+
+#include <avahi-common/address.h>
+#include "iface.h"
+
+AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i);
+void avahi_probe_scheduler_free(AvahiProbeScheduler *s);
+void avahi_probe_scheduler_clear(AvahiProbeScheduler *s);
+
+int avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, int immediately);
+
+#endif
diff --git a/trunk/avahi-core/publish.h b/trunk/avahi-core/publish.h
new file mode 100644
index 0000000..77c49af
--- /dev/null
+++ b/trunk/avahi-core/publish.h
@@ -0,0 +1,177 @@
+#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 core/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. */
+
+/** A group of locally registered DNS RRs */
+typedef struct AvahiSEntryGroup AvahiSEntryGroup;
+
+#include <avahi-common/cdecl.h>
+#include <avahi-core/core.h>
+
+AVAHI_C_DECL_BEGIN
+
+/** 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). */
+ AvahiPublishFlags flags, /**< Special flags for this record */
+ AvahiRecord *r /**< The record to add. This function increases the reference counter 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,
+ AvahiPublishFlags 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,
+ AvahiPublishFlags flags,
+ 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 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,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ const char *host,
+ uint16_t port,
+ AvahiStringList *strlst);
+
+/** Add a subtype for an already existing service */
+int avahi_server_add_service_subtype(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name, /**< Specify the name of main service you already added here */
+ const char *type, /**< Specify the main type of the service you already added here */
+ const char *domain, /**< Specify the main type of the service you already added here */
+ const char *subtype /**< The new subtype for the specified service */ );
+
+/** Update the TXT record for a service with the data from the specified string list */
+int avahi_server_update_service_txt_strlst(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ AvahiStringList *strlst);
+
+/** Update the TXT record for a service with the NULL termonate list of strings */
+int avahi_server_update_service_txt(
+ AvahiServer *s,
+ AvahiSEntryGroup *g,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiPublishFlags flags,
+ const char *name,
+ const char *type,
+ const char *domain,
+ ...) AVAHI_GCC_SENTINEL;
+
+/** Check if there is a service locally defined and return the entry group it is attached to. Returns NULL if the service isn't local*/
+int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/querier-test.c b/trunk/avahi-core/querier-test.c
new file mode 100644
index 0000000..1c28ea0
--- /dev/null
+++ b/trunk/avahi-core/querier-test.c
@@ -0,0 +1,124 @@
+/* $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-common/timeval.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";
+ }
+
+ 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,
+ AVAHI_GCC_UNUSED 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_RESULT_CACHED));
+}
+
+static void create_second_service_browser(AvahiTimeout *timeout, AVAHI_GCC_UNUSED 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(AVAHI_GCC_UNUSED AvahiTimeout *timeout, AVAHI_GCC_UNUSED void *userdata) {
+ avahi_simple_poll_quit(simple_poll);
+}
+
+int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED 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/trunk/avahi-core/querier.c b/trunk/avahi-core/querier.c
new file mode 100644
index 0000000..8a230cd
--- /dev/null
+++ b/trunk/avahi-core/querier.c
@@ -0,0 +1,271 @@
+/* $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 <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;
+
+ unsigned post_id;
+ int post_id_valid;
+
+ 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(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) {
+ AvahiQuerier *q = userdata;
+ struct timeval tv;
+
+ assert(q);
+
+ if (q->n_used <= 0) {
+
+ /* We are not referenced by anyone anymore, so let's free
+ * ourselves. We should not send out any further queries from
+ * this querier object anymore. */
+
+ avahi_querier_free(q);
+ return;
+ }
+
+ if (avahi_interface_post_query(q->interface, q->key, 0, &q->post_id)) {
+
+ /* The queue accepted our query. We store the query id here,
+ * that allows us to drop the query at a later point if the
+ * query is very short-lived. */
+
+ q->post_id_valid = 1;
+ }
+
+ 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. This is used for generating the
+ * ALL_FOR_NOW event one second after the querier was
+ * initially created. */
+ 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;
+ q->post_id_valid = 0;
+ gettimeofday(&q->creation_time, NULL);
+
+ /* Do the initial query */
+ if (avahi_interface_post_query(i, key, 0, &q->post_id))
+ q->post_id_valid = 1;
+
+ /* 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. This is used for generating the
+ * ALL_FOR_NOW event one second after the querier was initially
+ * created. */
+ 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)) || q->n_used <= 0) {
+ /* There was no querier for this RR key, or it wasn't referenced by anyone */
+ avahi_log_warn(__FILE__": querier_remove() called but no querier to remove.");
+ return;
+ }
+
+ if ((--q->n_used) <= 0) {
+
+ /* Nobody references us anymore. */
+
+ if (q->post_id_valid && avahi_interface_withraw_query(i, q->post_id)) {
+
+ /* We succeeded in withdrawing our query from the queue,
+ * so let's drop dead. */
+
+ avahi_querier_free(q);
+ }
+
+ /* If we failed to withdraw our query from the queue, we stay
+ * alive, in case someone else might recycle our querier at a
+ * later point. We are freed at our next expiry, in case
+ * nobody recycled us. */
+ }
+}
+
+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_shall_refresh_cache(AvahiInterface *i, AvahiKey *key) {
+ AvahiQuerier *q;
+
+ assert(i);
+ assert(key);
+
+ /* Called by the cache maintainer */
+
+ if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)))
+ /* This key is currently not subscribed at all, so no cache
+ * refresh is needed */
+ return 0;
+
+ if (q->n_used <= 0) {
+
+ /* If this is an entry nobody references right now, don't
+ * consider it "existing". */
+
+ /* Remove this querier since it is referenced by nobody
+ * and the cached data will soon be out of date */
+ avahi_querier_free(q);
+
+ /* Tell the cache that no refresh is needed */
+ return 0;
+
+ } else {
+ struct timeval tv;
+
+ /* We can defer our query a little, since the cache will now
+ * issue a refresh query anyway. */
+ avahi_elapse_time(&tv, q->sec_delay*1000, 0);
+ avahi_time_event_update(q->time_event, &tv);
+
+ /* Tell the cache that a refresh should be issued */
+ return 1;
+ }
+}
+
+void avahi_querier_free_all(AvahiInterface *i) {
+ assert(i);
+
+ while (i->queriers)
+ avahi_querier_free(i->queriers);
+}
diff --git a/trunk/avahi-core/querier.h b/trunk/avahi-core/querier.h
new file mode 100644
index 0000000..3f4eead
--- /dev/null
+++ b/trunk/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_shall_refresh_cache(AvahiInterface *i, AvahiKey *key);
+
+#endif
diff --git a/trunk/avahi-core/query-sched.c b/trunk/avahi-core/query-sched.c
new file mode 100644
index 0000000..0319b02
--- /dev/null
+++ b/trunk/avahi-core/query-sched.c
@@ -0,0 +1,452 @@
+/* $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 <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+
+#include "query-sched.h"
+#include "log.h"
+
+#define AVAHI_QUERY_HISTORY_MSEC 100
+#define AVAHI_QUERY_DEFER_MSEC 100
+
+typedef struct AvahiQueryJob AvahiQueryJob;
+typedef struct AvahiKnownAnswer AvahiKnownAnswer;
+
+struct AvahiQueryJob {
+ unsigned id;
+ int n_posted;
+
+ AvahiQueryScheduler *scheduler;
+ AvahiTimeEvent *time_event;
+
+ int done;
+ struct timeval delivery;
+
+ AvahiKey *key;
+
+ /* Jobs are stored in a simple linked list. It might turn out in
+ * the future that this list grows too long and we must switch to
+ * some other kind of data structure. This needs further
+ * investigation. I expect the list to be very short (< 20
+ * entries) most of the time, but this might be a wrong
+ * assumption, especially on setups where traffic reflection is
+ * involved. */
+
+ AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs);
+};
+
+struct AvahiKnownAnswer {
+ AvahiQueryScheduler *scheduler;
+ AvahiRecord *record;
+
+ AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer);
+};
+
+struct AvahiQueryScheduler {
+ AvahiInterface *interface;
+ AvahiTimeEventQueue *time_event_queue;
+
+ unsigned next_id;
+
+ AVAHI_LLIST_HEAD(AvahiQueryJob, jobs);
+ AVAHI_LLIST_HEAD(AvahiQueryJob, history);
+ AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers);
+};
+
+static AvahiQueryJob* job_new(AvahiQueryScheduler *s, AvahiKey *key, int done) {
+ AvahiQueryJob *qj;
+
+ assert(s);
+ assert(key);
+
+ if (!(qj = avahi_new(AvahiQueryJob, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ return NULL;
+ }
+
+ qj->scheduler = s;
+ qj->key = avahi_key_ref(key);
+ qj->time_event = NULL;
+ qj->n_posted = 1;
+ qj->id = s->next_id++;
+
+ if ((qj->done = done))
+ AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj);
+ else
+ AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->jobs, qj);
+
+ return qj;
+}
+
+static void job_free(AvahiQueryScheduler *s, AvahiQueryJob *qj) {
+ assert(s);
+ assert(qj);
+
+ if (qj->time_event)
+ avahi_time_event_free(qj->time_event);
+
+ if (qj->done)
+ AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->history, qj);
+ else
+ AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj);
+
+ avahi_key_unref(qj->key);
+ avahi_free(qj);
+}
+
+static void elapse_callback(AvahiTimeEvent *e, void* data);
+
+static void job_set_elapse_time(AvahiQueryScheduler *s, AvahiQueryJob *qj, unsigned msec, unsigned jitter) {
+ struct timeval tv;
+
+ assert(s);
+ assert(qj);
+
+ avahi_elapse_time(&tv, msec, jitter);
+
+ if (qj->time_event)
+ avahi_time_event_update(qj->time_event, &tv);
+ else
+ qj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, qj);
+}
+
+static void job_mark_done(AvahiQueryScheduler *s, AvahiQueryJob *qj) {
+ assert(s);
+ assert(qj);
+
+ assert(!qj->done);
+
+ AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj);
+ AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj);
+
+ qj->done = 1;
+
+ job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0);
+ gettimeofday(&qj->delivery, NULL);
+}
+
+AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i) {
+ AvahiQueryScheduler *s;
+ assert(i);
+
+ if (!(s = avahi_new(AvahiQueryScheduler, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ return NULL; /* OOM */
+ }
+
+ s->interface = i;
+ s->time_event_queue = i->monitor->server->time_event_queue;
+ s->next_id = 0;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->jobs);
+ AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->history);
+ AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers);
+
+ return s;
+}
+
+void avahi_query_scheduler_free(AvahiQueryScheduler *s) {
+ assert(s);
+
+ assert(!s->known_answers);
+ avahi_query_scheduler_clear(s);
+ avahi_free(s);
+}
+
+void avahi_query_scheduler_clear(AvahiQueryScheduler *s) {
+ assert(s);
+
+ while (s->jobs)
+ job_free(s, s->jobs);
+ while (s->history)
+ job_free(s, s->history);
+}
+
+static void* known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
+ AvahiQueryScheduler *s = userdata;
+ AvahiKnownAnswer *ka;
+
+ assert(c);
+ assert(pattern);
+ assert(e);
+ assert(s);
+
+ if (avahi_cache_entry_half_ttl(c, e))
+ return NULL;
+
+ if (!(ka = avahi_new0(AvahiKnownAnswer, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ return NULL;
+ }
+
+ ka->scheduler = s;
+ ka->record = avahi_record_ref(e->record);
+
+ AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka);
+ return NULL;
+}
+
+static int packet_add_query_job(AvahiQueryScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) {
+ assert(s);
+ assert(p);
+ assert(qj);
+
+ if (!avahi_dns_packet_append_key(p, qj->key, 0))
+ return 0;
+
+ /* Add all matching known answers to the list */
+ avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s);
+
+ job_mark_done(s, qj);
+
+ return 1;
+}
+
+static void append_known_answers_and_send(AvahiQueryScheduler *s, AvahiDnsPacket *p) {
+ AvahiKnownAnswer *ka;
+ unsigned n;
+ assert(s);
+ assert(p);
+
+ n = 0;
+
+ while ((ka = s->known_answers)) {
+ int too_large = 0;
+
+ while (!avahi_dns_packet_append_record(p, ka->record, 0, 0)) {
+
+ if (avahi_dns_packet_is_empty(p)) {
+ /* The record is too large to fit into one packet, so
+ there's no point in sending it. Better is letting
+ the owner of the record send it as a response. This
+ has the advantage of a cache refresh. */
+
+ too_large = 1;
+ break;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC);
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
+ avahi_interface_send_packet(s->interface, p);
+ avahi_dns_packet_free(p);
+
+ p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
+ n = 0;
+ }
+
+ AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka);
+ avahi_record_unref(ka->record);
+ avahi_free(ka);
+
+ if (!too_large)
+ n++;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
+ avahi_interface_send_packet(s->interface, p);
+ avahi_dns_packet_free(p);
+}
+
+static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) {
+ AvahiQueryJob *qj = data;
+ AvahiQueryScheduler *s;
+ AvahiDnsPacket *p;
+ unsigned n;
+ int b;
+
+ assert(qj);
+ s = qj->scheduler;
+
+ if (qj->done) {
+ /* Lets remove it from the history */
+ job_free(s, qj);
+ return;
+ }
+
+ assert(!s->known_answers);
+
+ if (!(p = avahi_dns_packet_new_query(s->interface->hardware->mtu)))
+ return; /* OOM */
+
+ b = packet_add_query_job(s, p, qj);
+ assert(b); /* An query must always fit in */
+ n = 1;
+
+ /* Try to fill up packet with more queries, if available */
+ while (s->jobs) {
+
+ if (!packet_add_query_job(s, p, s->jobs))
+ break;
+
+ n++;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
+
+ /* Now add known answers */
+ append_known_answers_and_send(s, p);
+}
+
+static AvahiQueryJob* find_scheduled_job(AvahiQueryScheduler *s, AvahiKey *key) {
+ AvahiQueryJob *qj;
+
+ assert(s);
+ assert(key);
+
+ for (qj = s->jobs; qj; qj = qj->jobs_next) {
+ assert(!qj->done);
+
+ if (avahi_key_equal(qj->key, key))
+ return qj;
+ }
+
+ return NULL;
+}
+
+static AvahiQueryJob* find_history_job(AvahiQueryScheduler *s, AvahiKey *key) {
+ AvahiQueryJob *qj;
+
+ assert(s);
+ assert(key);
+
+ for (qj = s->history; qj; qj = qj->jobs_next) {
+ assert(qj->done);
+
+ if (avahi_key_equal(qj->key, key)) {
+ /* Check whether this entry is outdated */
+
+ if (avahi_age(&qj->delivery) > AVAHI_QUERY_HISTORY_MSEC*1000) {
+ /* it is outdated, so let's remove it */
+ job_free(s, qj);
+ return NULL;
+ }
+
+ return qj;
+ }
+ }
+
+ return NULL;
+}
+
+int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id) {
+ struct timeval tv;
+ AvahiQueryJob *qj;
+
+ assert(s);
+ assert(key);
+
+ if ((qj = find_history_job(s, key)))
+ return 0;
+
+ avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0);
+
+ if ((qj = find_scheduled_job(s, key))) {
+ /* Duplicate questions suppression */
+
+ if (avahi_timeval_compare(&tv, &qj->delivery) < 0) {
+ /* If the new entry should be scheduled earlier,
+ * update the old entry */
+ qj->delivery = tv;
+ avahi_time_event_update(qj->time_event, &qj->delivery);
+ }
+
+ qj->n_posted++;
+
+ } else {
+
+ if (!(qj = job_new(s, key, 0)))
+ return 0; /* OOM */
+
+ qj->delivery = tv;
+ qj->time_event = avahi_time_event_new(s->time_event_queue, &qj->delivery, elapse_callback, qj);
+ }
+
+ if (ret_id)
+ *ret_id = qj->id;
+
+ return 1;
+}
+
+void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) {
+ AvahiQueryJob *qj;
+
+ assert(s);
+ assert(key);
+
+ /* This function is called whenever an incoming query was
+ * received. We drop scheduled queries that match. The keyword is
+ * "DUPLICATE QUESTION SUPPRESION". */
+
+ if ((qj = find_scheduled_job(s, key))) {
+ job_mark_done(s, qj);
+ return;
+ }
+
+ /* Look if there's a history job for this key. If there is, just
+ * update the elapse time */
+ if (!(qj = find_history_job(s, key)))
+ if (!(qj = job_new(s, key, 1)))
+ return; /* OOM */
+
+ gettimeofday(&qj->delivery, NULL);
+ job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0);
+}
+
+int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id) {
+ AvahiQueryJob *qj;
+
+ assert(s);
+
+ /* Very short lived queries can withdraw an already scheduled item
+ * from the queue using this function, simply by passing the id
+ * returned by avahi_query_scheduler_post(). */
+
+ for (qj = s->jobs; qj; qj = qj->jobs_next) {
+ assert(!qj->done);
+
+ if (qj->id == id) {
+ /* Entry found */
+
+ assert(qj->n_posted >= 1);
+
+ if (--qj->n_posted <= 0) {
+
+ /* We withdraw this job only if the calling object was
+ * the only remaining poster. (Usually this is the
+ * case since there should exist only one querier per
+ * key, but there are exceptions, notably reflected
+ * traffic.) */
+
+ job_free(s, qj);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/trunk/avahi-core/query-sched.h b/trunk/avahi-core/query-sched.h
new file mode 100644
index 0000000..5238558
--- /dev/null
+++ b/trunk/avahi-core/query-sched.h
@@ -0,0 +1,38 @@
+#ifndef fooqueryschedhfoo
+#define fooqueryschedhfoo
+
+/* $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 AvahiQueryScheduler AvahiQueryScheduler;
+
+#include <avahi-common/address.h>
+#include "iface.h"
+
+AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i);
+void avahi_query_scheduler_free(AvahiQueryScheduler *s);
+void avahi_query_scheduler_clear(AvahiQueryScheduler *s);
+
+int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id);
+int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id);
+void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key);
+
+#endif
diff --git a/trunk/avahi-core/resolve-address.c b/trunk/avahi-core/resolve-address.c
new file mode 100644
index 0000000..25d21ac
--- /dev/null
+++ b/trunk/avahi-core/resolve-address.c
@@ -0,0 +1,270 @@
+/* $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 <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+#include <avahi-common/domain.h>
+
+#include "browse.h"
+
+#define TIMEOUT_MSEC 5000
+
+struct AvahiSAddressResolver {
+ AvahiServer *server;
+ AvahiAddress address;
+
+ AvahiSRecordBrowser *record_browser;
+
+ AvahiSAddressResolverCallback callback;
+ void* userdata;
+
+ AvahiRecord *ptr_record;
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ AvahiLookupResultFlags flags;
+
+ int retry_with_multicast;
+ AvahiKey *key;
+
+ AvahiTimeEvent *time_event;
+
+ AVAHI_LLIST_FIELDS(AvahiSAddressResolver, resolver);
+};
+
+static void finish(AvahiSAddressResolver *r, AvahiResolverEvent event) {
+ assert(r);
+
+ if (r->time_event) {
+ avahi_time_event_free(r->time_event);
+ r->time_event = NULL;
+ }
+
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+ r->callback(r, r->interface, r->protocol, event, &r->address, NULL, r->flags, r->userdata);
+ break;
+
+ case AVAHI_RESOLVER_FOUND:
+ assert(r->ptr_record);
+ r->callback(r, r->interface, r->protocol, event, &r->address, r->ptr_record->data.ptr.name, r->flags, r->userdata);
+ break;
+ }
+}
+
+static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
+ AvahiSAddressResolver *r = userdata;
+
+ assert(e);
+ assert(r);
+
+ avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
+ finish(r, AVAHI_RESOLVER_FAILURE);
+}
+
+static void start_timeout(AvahiSAddressResolver *r) {
+ struct timeval tv;
+ assert(r);
+
+ if (r->time_event)
+ return;
+
+ avahi_elapse_time(&tv, TIMEOUT_MSEC, 0);
+ r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r);
+}
+
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ AvahiSAddressResolver *r = userdata;
+
+ assert(rr);
+ assert(r);
+
+ switch (event) {
+ case AVAHI_BROWSER_NEW:
+ assert(record);
+ assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+ if (r->interface > 0 && interface != r->interface)
+ return;
+
+ if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
+ return;
+
+ if (r->interface <= 0)
+ r->interface = interface;
+
+ if (r->protocol == AVAHI_PROTO_UNSPEC)
+ r->protocol = protocol;
+
+ if (!r->ptr_record) {
+ r->ptr_record = avahi_record_ref(record);
+ r->flags = flags;
+
+ finish(r, AVAHI_RESOLVER_FOUND);
+ }
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ assert(record);
+ assert(record->key->type == AVAHI_DNS_TYPE_PTR);
+
+ if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) {
+ avahi_record_unref(r->ptr_record);
+ r->ptr_record = NULL;
+ r->flags = flags;
+
+ /** Look for a replacement */
+ avahi_s_record_browser_restart(r->record_browser);
+ start_timeout(r);
+ }
+
+ break;
+
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ break;
+
+ case AVAHI_BROWSER_FAILURE:
+
+ if (r->retry_with_multicast) {
+ r->retry_with_multicast = 0;
+
+ avahi_s_record_browser_free(r->record_browser);
+ r->record_browser = avahi_s_record_browser_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;
+ }
+ }
+
+ r->flags = flags;
+ finish(r, AVAHI_RESOLVER_FAILURE);
+ break;
+ }
+}
+
+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[AVAHI_DOMAIN_NAME_MAX];
+
+ assert(server);
+ assert(address);
+ assert(callback);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, address->proto == AVAHI_PROTO_INET || address->proto == AVAHI_PROTO_INET6, AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+ avahi_reverse_lookup_name(address, n, sizeof(n));
+
+ if (!(k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ if (!(r = avahi_new(AvahiSAddressResolver, 1))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ avahi_key_unref(k);
+ return NULL;
+ }
+
+ r->server = server;
+ r->address = *address;
+ r->callback = callback;
+ r->userdata = userdata;
+ r->ptr_record = NULL;
+ r->interface = interface;
+ r->protocol = protocol;
+ r->flags = 0;
+ r->retry_with_multicast = 0;
+ r->key = k;
+
+ r->record_browser = NULL;
+ AVAHI_LLIST_PREPEND(AvahiSAddressResolver, resolver, server->address_resolvers, r);
+
+ r->time_event = NULL;
+
+ if (!(flags & (AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_USE_WIDE_AREA))) {
+
+ if (!server->wide_area_lookup_engine || !avahi_wide_area_has_servers(server->wide_area_lookup_engine))
+ flags |= AVAHI_LOOKUP_USE_MULTICAST;
+ else {
+ flags |= AVAHI_LOOKUP_USE_WIDE_AREA;
+ r->retry_with_multicast = 1;
+ }
+ }
+
+ r->record_browser = avahi_s_record_browser_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;
+}
+
+void avahi_s_address_resolver_free(AvahiSAddressResolver *r) {
+ assert(r);
+
+ AVAHI_LLIST_REMOVE(AvahiSAddressResolver, resolver, r->server->address_resolvers, r);
+
+ if (r->record_browser)
+ avahi_s_record_browser_free(r->record_browser);
+
+ if (r->time_event)
+ avahi_time_event_free(r->time_event);
+
+ if (r->ptr_record)
+ avahi_record_unref(r->ptr_record);
+
+ if (r->key)
+ avahi_key_unref(r->key);
+
+ avahi_free(r);
+}
diff --git a/trunk/avahi-core/resolve-host-name.c b/trunk/avahi-core/resolve-host-name.c
new file mode 100644
index 0000000..59dc3cf
--- /dev/null
+++ b/trunk/avahi-core/resolve-host-name.c
@@ -0,0 +1,299 @@
+/* $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 <avahi-common/domain.h>
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+#define TIMEOUT_MSEC 5000
+
+struct AvahiSHostNameResolver {
+ AvahiServer *server;
+ char *host_name;
+
+ AvahiSRecordBrowser *record_browser_a;
+ AvahiSRecordBrowser *record_browser_aaaa;
+
+ AvahiSHostNameResolverCallback callback;
+ void* userdata;
+
+ AvahiRecord *address_record;
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+ AvahiLookupResultFlags flags;
+
+ AvahiTimeEvent *time_event;
+
+ AVAHI_LLIST_FIELDS(AvahiSHostNameResolver, resolver);
+};
+
+static void finish(AvahiSHostNameResolver *r, AvahiResolverEvent event) {
+ assert(r);
+
+ if (r->time_event) {
+ avahi_time_event_free(r->time_event);
+ r->time_event = NULL;
+ }
+
+ switch (event) {
+ case AVAHI_RESOLVER_FOUND: {
+ AvahiAddress a;
+
+ assert(r->address_record);
+
+ switch (r->address_record->key->type) {
+ case AVAHI_DNS_TYPE_A:
+ a.proto = AVAHI_PROTO_INET;
+ a.data.ipv4 = r->address_record->data.a.address;
+ break;
+
+ case AVAHI_DNS_TYPE_AAAA:
+ a.proto = AVAHI_PROTO_INET6;
+ a.data.ipv6 = r->address_record->data.aaaa.address;
+ break;
+
+ default:
+ abort();
+ }
+
+ r->callback(r, r->interface, r->protocol, AVAHI_RESOLVER_FOUND, r->address_record->key->name, &a, r->flags, r->userdata);
+ break;
+
+ }
+
+ case AVAHI_RESOLVER_FAILURE:
+
+ r->callback(r, r->interface, r->protocol, event, r->host_name, NULL, r->flags, r->userdata);
+ break;
+ }
+}
+
+static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
+ AvahiSHostNameResolver *r = userdata;
+
+ assert(e);
+ assert(r);
+
+ avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
+ finish(r, AVAHI_RESOLVER_FAILURE);
+}
+
+static void start_timeout(AvahiSHostNameResolver *r) {
+ struct timeval tv;
+ assert(r);
+
+ if (r->time_event)
+ return;
+
+ avahi_elapse_time(&tv, TIMEOUT_MSEC, 0);
+
+ r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r);
+}
+
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ AvahiSHostNameResolver *r = userdata;
+
+ assert(rr);
+ assert(r);
+
+
+ switch (event) {
+ case AVAHI_BROWSER_NEW:
+ assert(record);
+ assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA);
+
+ if (r->interface > 0 && interface != r->interface)
+ return;
+
+ if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
+ return;
+
+ if (r->interface <= 0)
+ r->interface = interface;
+
+ if (r->protocol == AVAHI_PROTO_UNSPEC)
+ r->protocol = protocol;
+
+ if (!r->address_record) {
+ r->address_record = avahi_record_ref(record);
+ r->flags = flags;
+
+ finish(r, AVAHI_RESOLVER_FOUND);
+ }
+
+ break;
+
+ case AVAHI_BROWSER_REMOVE:
+ assert(record);
+ assert(record->key->type == AVAHI_DNS_TYPE_A || record->key->type == AVAHI_DNS_TYPE_AAAA);
+
+ if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) {
+ avahi_record_unref(r->address_record);
+ r->address_record = NULL;
+
+ r->flags = flags;
+
+
+ /** Look for a replacement */
+ if (r->record_browser_aaaa)
+ avahi_s_record_browser_restart(r->record_browser_aaaa);
+ if (r->record_browser_a)
+ avahi_s_record_browser_restart(r->record_browser_a);
+
+ start_timeout(r);
+ }
+
+ break;
+
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ /* Ignore */
+ break;
+
+ case AVAHI_BROWSER_FAILURE:
+
+ /* Stop browsers */
+
+ if (r->record_browser_aaaa)
+ avahi_s_record_browser_free(r->record_browser_aaaa);
+ if (r->record_browser_a)
+ avahi_s_record_browser_free(r->record_browser_a);
+
+ r->record_browser_a = r->record_browser_aaaa = NULL;
+ r->flags = flags;
+
+ finish(r, AVAHI_RESOLVER_FAILURE);
+ break;
+ }
+}
+
+AvahiSHostNameResolver *avahi_s_host_name_resolver_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);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_fqdn(host_name), AVAHI_ERR_INVALID_HOST_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
+
+ if (!(r = avahi_new(AvahiSHostNameResolver, 1))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ r->server = server;
+ r->host_name = avahi_normalize_name_strdup(host_name);
+ r->callback = callback;
+ r->userdata = userdata;
+ r->address_record = NULL;
+ r->interface = interface;
+ r->protocol = protocol;
+ r->flags = 0;
+
+ r->record_browser_a = r->record_browser_aaaa = NULL;
+
+ r->time_event = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiSHostNameResolver, resolver, server->host_name_resolvers, r);
+
+ r->record_browser_aaaa = r->record_browser_a = NULL;
+
+ if (aprotocol == AVAHI_PROTO_INET || aprotocol == AVAHI_PROTO_UNSPEC) {
+ k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
+ r->record_browser_a = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r);
+ avahi_key_unref(k);
+
+ if (!r->record_browser_a)
+ goto fail;
+ }
+
+ if (aprotocol == AVAHI_PROTO_INET6 || aprotocol == AVAHI_PROTO_UNSPEC) {
+ k = avahi_key_new(host_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
+ r->record_browser_aaaa = avahi_s_record_browser_new(server, interface, protocol, k, flags, record_browser_callback, r);
+ avahi_key_unref(k);
+
+ if (!r->record_browser_aaaa)
+ goto fail;
+ }
+
+ assert(r->record_browser_aaaa || r->record_browser_a);
+
+ start_timeout(r);
+
+ return r;
+
+fail:
+ avahi_s_host_name_resolver_free(r);
+ return NULL;
+}
+
+void avahi_s_host_name_resolver_free(AvahiSHostNameResolver *r) {
+ assert(r);
+
+ AVAHI_LLIST_REMOVE(AvahiSHostNameResolver, resolver, r->server->host_name_resolvers, r);
+
+ if (r->record_browser_a)
+ avahi_s_record_browser_free(r->record_browser_a);
+
+ if (r->record_browser_aaaa)
+ avahi_s_record_browser_free(r->record_browser_aaaa);
+
+ if (r->time_event)
+ avahi_time_event_free(r->time_event);
+
+ if (r->address_record)
+ avahi_record_unref(r->address_record);
+
+ avahi_free(r->host_name);
+ avahi_free(r);
+}
diff --git a/trunk/avahi-core/resolve-service.c b/trunk/avahi-core/resolve-service.c
new file mode 100644
index 0000000..1ad6078
--- /dev/null
+++ b/trunk/avahi-core/resolve-service.c
@@ -0,0 +1,491 @@
+/* $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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "browse.h"
+#include "log.h"
+
+#define TIMEOUT_MSEC 5000
+
+struct AvahiSServiceResolver {
+ AvahiServer *server;
+ char *service_name;
+ char *service_type;
+ char *domain_name;
+ AvahiProtocol address_protocol;
+
+ AvahiIfIndex interface;
+ AvahiProtocol protocol;
+
+ AvahiSRecordBrowser *record_browser_srv;
+ AvahiSRecordBrowser *record_browser_txt;
+ AvahiSRecordBrowser *record_browser_a;
+ AvahiSRecordBrowser *record_browser_aaaa;
+
+ AvahiRecord *srv_record, *txt_record, *address_record;
+ AvahiLookupResultFlags srv_flags, txt_flags, address_flags;
+
+ AvahiSServiceResolverCallback callback;
+ void* userdata;
+ AvahiLookupFlags user_flags;
+
+ AvahiTimeEvent *time_event;
+
+ AVAHI_LLIST_FIELDS(AvahiSServiceResolver, resolver);
+};
+
+static void finish(AvahiSServiceResolver *r, AvahiResolverEvent event) {
+ AvahiLookupResultFlags flags;
+
+ assert(r);
+
+ if (r->time_event) {
+ avahi_time_event_free(r->time_event);
+ r->time_event = NULL;
+ }
+
+ flags =
+ r->txt_flags |
+ r->srv_flags |
+ r->address_flags;
+
+ switch (event) {
+ case AVAHI_RESOLVER_FAILURE:
+
+ r->callback(
+ r,
+ r->interface,
+ r->protocol,
+ event,
+ r->service_name,
+ r->service_type,
+ r->domain_name,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ flags,
+ r->userdata);
+
+ break;
+
+ case AVAHI_RESOLVER_FOUND: {
+ AvahiAddress a;
+
+ assert(event == AVAHI_RESOLVER_FOUND);
+
+ assert(r->srv_record);
+
+ if (r->address_record) {
+ switch (r->address_record->key->type) {
+ case AVAHI_DNS_TYPE_A:
+ a.proto = AVAHI_PROTO_INET;
+ a.data.ipv4 = r->address_record->data.a.address;
+ break;
+
+ case AVAHI_DNS_TYPE_AAAA:
+ a.proto = AVAHI_PROTO_INET6;
+ a.data.ipv6 = r->address_record->data.aaaa.address;
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+
+ r->callback(
+ r,
+ r->interface,
+ r->protocol,
+ event,
+ r->service_name,
+ r->service_type,
+ r->domain_name,
+ r->srv_record->data.srv.name,
+ r->address_record ? &a : NULL,
+ r->srv_record->data.srv.port,
+ r->txt_record ? r->txt_record->data.txt.string_list : NULL,
+ flags,
+ r->userdata);
+
+ break;
+ }
+ }
+}
+
+static void time_event_callback(AvahiTimeEvent *e, void *userdata) {
+ AvahiSServiceResolver *r = userdata;
+
+ assert(e);
+ assert(r);
+
+ avahi_server_set_errno(r->server, AVAHI_ERR_TIMEOUT);
+ finish(r, AVAHI_RESOLVER_FAILURE);
+}
+
+static void start_timeout(AvahiSServiceResolver *r) {
+ struct timeval tv;
+ assert(r);
+
+ if (r->time_event)
+ return;
+
+ avahi_elapse_time(&tv, TIMEOUT_MSEC, 0);
+
+ r->time_event = avahi_time_event_new(r->server->time_event_queue, &tv, time_event_callback, r);
+}
+
+static void record_browser_callback(
+ AvahiSRecordBrowser*rr,
+ AvahiIfIndex interface,
+ AvahiProtocol protocol,
+ AvahiBrowserEvent event,
+ AvahiRecord *record,
+ AvahiLookupResultFlags flags,
+ void* userdata) {
+
+ AvahiSServiceResolver *r = userdata;
+
+ assert(rr);
+ assert(r);
+
+ if (rr == r->record_browser_aaaa || rr == r->record_browser_a)
+ r->address_flags = flags;
+ else if (rr == r->record_browser_srv)
+ r->srv_flags = flags;
+ else if (rr == r->record_browser_txt)
+ r->txt_flags = flags;
+
+ switch (event) {
+
+ case AVAHI_BROWSER_NEW: {
+ int changed = 0;
+ assert(record);
+
+ if (r->interface > 0 && interface > 0 && interface != r->interface)
+ return;
+
+ if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol)
+ return;
+
+ if (r->interface <= 0)
+ r->interface = interface;
+
+ if (r->protocol == AVAHI_PROTO_UNSPEC)
+ r->protocol = protocol;
+
+ switch (record->key->type) {
+ case AVAHI_DNS_TYPE_SRV:
+ if (!r->srv_record) {
+ r->srv_record = avahi_record_ref(record);
+ changed = 1;
+
+ if (r->record_browser_a) {
+ avahi_s_record_browser_free(r->record_browser_a);
+ r->record_browser_a = NULL;
+ }
+
+ if (r->record_browser_aaaa) {
+ avahi_s_record_browser_free(r->record_browser_aaaa);
+ r->record_browser_aaaa = NULL;
+ }
+
+ if (!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)) {
+
+ if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) {
+ AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
+ r->record_browser_a = avahi_s_record_browser_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:
+
+ assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT));
+
+ if (!r->txt_record) {
+ r->txt_record = avahi_record_ref(record);
+ changed = 1;
+ }
+ break;
+
+ case AVAHI_DNS_TYPE_A:
+ case AVAHI_DNS_TYPE_AAAA:
+
+ assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS));
+
+ if (!r->address_record) {
+ r->address_record = avahi_record_ref(record);
+ changed = 1;
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+
+ if (changed &&
+ r->srv_record &&
+ (r->txt_record || (r->user_flags & AVAHI_LOOKUP_NO_TXT)) &&
+ (r->address_record || (r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)))
+ finish(r, AVAHI_RESOLVER_FOUND);
+
+ break;
+
+ }
+
+ case AVAHI_BROWSER_REMOVE:
+
+ assert(record);
+
+ switch (record->key->type) {
+ case AVAHI_DNS_TYPE_SRV:
+
+ if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) {
+ avahi_record_unref(r->srv_record);
+ r->srv_record = NULL;
+
+ if (r->record_browser_a) {
+ avahi_s_record_browser_free(r->record_browser_a);
+ r->record_browser_a = NULL;
+ }
+
+ if (r->record_browser_aaaa) {
+ avahi_s_record_browser_free(r->record_browser_aaaa);
+ r->record_browser_aaaa = NULL;
+ }
+
+ /** Look for a replacement */
+ avahi_s_record_browser_restart(r->record_browser_srv);
+ start_timeout(r);
+ }
+
+ break;
+
+ case AVAHI_DNS_TYPE_TXT:
+
+ assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT));
+
+ if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) {
+ avahi_record_unref(r->txt_record);
+ r->txt_record = NULL;
+
+ /** Look for a replacement */
+ avahi_s_record_browser_restart(r->record_browser_txt);
+ start_timeout(r);
+ }
+ break;
+
+ case AVAHI_DNS_TYPE_A:
+ case AVAHI_DNS_TYPE_AAAA:
+
+ assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS));
+
+ if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) {
+ avahi_record_unref(r->address_record);
+ r->address_record = NULL;
+
+ /** Look for a replacement */
+ if (r->record_browser_aaaa)
+ avahi_s_record_browser_restart(r->record_browser_aaaa);
+ if (r->record_browser_a)
+ avahi_s_record_browser_restart(r->record_browser_a);
+ start_timeout(r);
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ break;
+
+ case AVAHI_BROWSER_CACHE_EXHAUSTED:
+ case AVAHI_BROWSER_ALL_FOR_NOW:
+ break;
+
+ case AVAHI_BROWSER_FAILURE:
+
+ if (rr == r->record_browser_a && r->record_browser_aaaa) {
+ /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */
+ avahi_s_record_browser_free(r->record_browser_a);
+ r->record_browser_a = NULL;
+ break;
+ }
+
+ if (rr == r->record_browser_aaaa && r->record_browser_a) {
+ /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */
+ avahi_s_record_browser_free(r->record_browser_aaaa);
+ r->record_browser_aaaa = NULL;
+ break;
+ }
+
+ /* Hmm, everything's lost, tell the user */
+
+ if (r->record_browser_srv)
+ avahi_s_record_browser_free(r->record_browser_srv);
+ if (r->record_browser_txt)
+ avahi_s_record_browser_free(r->record_browser_txt);
+ if (r->record_browser_a)
+ avahi_s_record_browser_free(r->record_browser_a);
+ if (r->record_browser_aaaa)
+ avahi_s_record_browser_free(r->record_browser_aaaa);
+
+ r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL;
+
+ finish(r, AVAHI_RESOLVER_FAILURE);
+ break;
+ }
+}
+
+AvahiSServiceResolver *avahi_s_service_resolver_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 n[AVAHI_DOMAIN_NAME_MAX];
+ int ret;
+
+ assert(server);
+ assert(type);
+ assert(callback);
+
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_PROTO_VALID(aprotocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, !name || avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+ AVAHI_CHECK_VALIDITY_RETURN_NULL(server, AVAHI_FLAGS_VALID(flags, AVAHI_LOOKUP_USE_WIDE_AREA|AVAHI_LOOKUP_USE_MULTICAST|AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), AVAHI_ERR_INVALID_FLAGS);
+
+ if (!domain)
+ domain = server->domain_name;
+
+ if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain)) < 0) {
+ avahi_server_set_errno(server, ret);
+ return NULL;
+ }
+
+ if (!(r = avahi_new(AvahiSServiceResolver, 1))) {
+ avahi_server_set_errno(server, AVAHI_ERR_NO_MEMORY);
+ return NULL;
+ }
+
+ r->server = server;
+ r->service_name = avahi_strdup(name);
+ r->service_type = avahi_normalize_name_strdup(type);
+ r->domain_name = avahi_normalize_name_strdup(domain);
+ r->callback = callback;
+ r->userdata = userdata;
+ r->address_protocol = aprotocol;
+ r->srv_record = r->txt_record = r->address_record = NULL;
+ r->srv_flags = r->txt_flags = r->address_flags = 0;
+ r->interface = interface;
+ r->protocol = protocol;
+ r->user_flags = flags;
+ r->record_browser_a = r->record_browser_aaaa = r->record_browser_srv = r->record_browser_txt = NULL;
+ r->time_event = NULL;
+ AVAHI_LLIST_PREPEND(AvahiSServiceResolver, resolver, server->service_resolvers, r);
+
+ k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
+ r->record_browser_srv = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r);
+ avahi_key_unref(k);
+
+ if (!r->record_browser_srv) {
+ avahi_s_service_resolver_free(r);
+ return NULL;
+ }
+
+ if (!(flags & AVAHI_LOOKUP_NO_TXT)) {
+ k = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
+ r->record_browser_txt = avahi_s_record_browser_new(server, interface, protocol, k, flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r);
+ avahi_key_unref(k);
+
+ if (!r->record_browser_txt) {
+ avahi_s_service_resolver_free(r);
+ return NULL;
+ }
+ }
+
+ start_timeout(r);
+
+ return r;
+}
+
+void avahi_s_service_resolver_free(AvahiSServiceResolver *r) {
+ assert(r);
+
+ AVAHI_LLIST_REMOVE(AvahiSServiceResolver, resolver, r->server->service_resolvers, r);
+
+ if (r->time_event)
+ avahi_time_event_free(r->time_event);
+
+ if (r->record_browser_srv)
+ avahi_s_record_browser_free(r->record_browser_srv);
+ if (r->record_browser_txt)
+ avahi_s_record_browser_free(r->record_browser_txt);
+ if (r->record_browser_a)
+ avahi_s_record_browser_free(r->record_browser_a);
+ if (r->record_browser_aaaa)
+ avahi_s_record_browser_free(r->record_browser_aaaa);
+
+ if (r->srv_record)
+ avahi_record_unref(r->srv_record);
+ if (r->txt_record)
+ avahi_record_unref(r->txt_record);
+ if (r->address_record)
+ avahi_record_unref(r->address_record);
+
+ avahi_free(r->service_name);
+ avahi_free(r->service_type);
+ avahi_free(r->domain_name);
+ avahi_free(r);
+}
diff --git a/trunk/avahi-core/response-sched.c b/trunk/avahi-core/response-sched.c
new file mode 100644
index 0000000..ef10eca
--- /dev/null
+++ b/trunk/avahi-core/response-sched.c
@@ -0,0 +1,516 @@
+/* $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 <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+
+#include "response-sched.h"
+#include "log.h"
+#include "rr-util.h"
+
+/* Local packets are supressed this long after sending them */
+#define AVAHI_RESPONSE_HISTORY_MSEC 500
+
+/* Local packets are deferred this long before sending them */
+#define AVAHI_RESPONSE_DEFER_MSEC 20
+
+/* Additional jitter for deferred packets */
+#define AVAHI_RESPONSE_JITTER_MSEC 100
+
+/* Remote packets can suppress local traffic as long as this value */
+#define AVAHI_RESPONSE_SUPPRESS_MSEC 700
+
+typedef struct AvahiResponseJob AvahiResponseJob;
+
+typedef enum {
+ AVAHI_SCHEDULED,
+ AVAHI_DONE,
+ AVAHI_SUPPRESSED
+} AvahiResponseJobState;
+
+struct AvahiResponseJob {
+ AvahiResponseScheduler *scheduler;
+ AvahiTimeEvent *time_event;
+
+ AvahiResponseJobState state;
+ struct timeval delivery;
+
+ AvahiRecord *record;
+ int flush_cache;
+ AvahiAddress querier;
+ int querier_valid;
+
+ AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs);
+};
+
+struct AvahiResponseScheduler {
+ AvahiInterface *interface;
+ AvahiTimeEventQueue *time_event_queue;
+
+ AVAHI_LLIST_HEAD(AvahiResponseJob, jobs);
+ AVAHI_LLIST_HEAD(AvahiResponseJob, history);
+ AVAHI_LLIST_HEAD(AvahiResponseJob, suppressed);
+};
+
+static AvahiResponseJob* job_new(AvahiResponseScheduler *s, AvahiRecord *record, AvahiResponseJobState state) {
+ AvahiResponseJob *rj;
+
+ assert(s);
+ assert(record);
+
+ if (!(rj = avahi_new(AvahiResponseJob, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ return NULL;
+ }
+
+ rj->scheduler = s;
+ rj->record = avahi_record_ref(record);
+ rj->time_event = NULL;
+ rj->flush_cache = 0;
+ rj->querier_valid = 0;
+
+ if ((rj->state = state) == AVAHI_SCHEDULED)
+ AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->jobs, rj);
+ else if (rj->state == AVAHI_DONE)
+ AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj);
+ else /* rj->state == AVAHI_SUPPRESSED */
+ AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->suppressed, rj);
+
+ return rj;
+}
+
+static void job_free(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
+ assert(s);
+ assert(rj);
+
+ if (rj->time_event)
+ avahi_time_event_free(rj->time_event);
+
+ if (rj->state == AVAHI_SCHEDULED)
+ AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj);
+ else if (rj->state == AVAHI_DONE)
+ AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->history, rj);
+ else /* rj->state == AVAHI_SUPPRESSED */
+ AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->suppressed, rj);
+
+ avahi_record_unref(rj->record);
+ avahi_free(rj);
+}
+
+static void elapse_callback(AvahiTimeEvent *e, void* data);
+
+static void job_set_elapse_time(AvahiResponseScheduler *s, AvahiResponseJob *rj, unsigned msec, unsigned jitter) {
+ struct timeval tv;
+
+ assert(s);
+ assert(rj);
+
+ avahi_elapse_time(&tv, msec, jitter);
+
+ if (rj->time_event)
+ avahi_time_event_update(rj->time_event, &tv);
+ else
+ rj->time_event = avahi_time_event_new(s->time_event_queue, &tv, elapse_callback, rj);
+}
+
+static void job_mark_done(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
+ assert(s);
+ assert(rj);
+
+ assert(rj->state == AVAHI_SCHEDULED);
+
+ AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj);
+ AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj);
+
+ rj->state = AVAHI_DONE;
+
+ job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0);
+
+ gettimeofday(&rj->delivery, NULL);
+}
+
+AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i) {
+ AvahiResponseScheduler *s;
+ assert(i);
+
+ if (!(s = avahi_new(AvahiResponseScheduler, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ return NULL;
+ }
+
+ s->interface = i;
+ s->time_event_queue = i->monitor->server->time_event_queue;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->jobs);
+ AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->history);
+ AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->suppressed);
+
+ return s;
+}
+
+void avahi_response_scheduler_free(AvahiResponseScheduler *s) {
+ assert(s);
+
+ avahi_response_scheduler_clear(s);
+ avahi_free(s);
+}
+
+void avahi_response_scheduler_clear(AvahiResponseScheduler *s) {
+ assert(s);
+
+ while (s->jobs)
+ job_free(s, s->jobs);
+ while (s->history)
+ job_free(s, s->history);
+ while (s->suppressed)
+ job_free(s, s->suppressed);
+}
+
+static void enumerate_aux_records_callback(AVAHI_GCC_UNUSED AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
+ AvahiResponseJob *rj = userdata;
+
+ assert(r);
+ assert(rj);
+
+ avahi_response_scheduler_post(rj->scheduler, r, flush_cache, rj->querier_valid ? &rj->querier : NULL, 0);
+}
+
+static int packet_add_response_job(AvahiResponseScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) {
+ assert(s);
+ assert(p);
+ assert(rj);
+
+ /* Try to add this record to the packet */
+ if (!avahi_dns_packet_append_record(p, rj->record, rj->flush_cache, 0))
+ return 0;
+
+ /* Ok, this record will definitely be sent, so schedule the
+ * auxilliary packets, too */
+ avahi_server_enumerate_aux_records(s->interface->monitor->server, s->interface, rj->record, enumerate_aux_records_callback, rj);
+ job_mark_done(s, rj);
+
+ return 1;
+}
+
+static void send_response_packet(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
+ AvahiDnsPacket *p;
+ unsigned n;
+
+ assert(s);
+ assert(rj);
+
+ if (!(p = avahi_dns_packet_new_response(s->interface->hardware->mtu, 1)))
+ return; /* OOM */
+ n = 1;
+
+ /* Put it in the packet. */
+ if (packet_add_response_job(s, p, rj)) {
+
+ /* Try to fill up packet with more responses, if available */
+ while (s->jobs) {
+
+ if (!packet_add_response_job(s, p, s->jobs))
+ break;
+
+ n++;
+ }
+
+ } else {
+ size_t size;
+
+ avahi_dns_packet_free(p);
+
+ /* OK, the packet was too small, so create one that fits */
+ size = avahi_record_get_estimate_size(rj->record) + AVAHI_DNS_PACKET_HEADER_SIZE;
+
+ if (size > AVAHI_DNS_PACKET_SIZE_MAX)
+ size = AVAHI_DNS_PACKET_SIZE_MAX;
+
+ if (!(p = avahi_dns_packet_new_response(size, 1)))
+ return; /* OOM */
+
+ if (!packet_add_response_job(s, p, rj)) {
+ avahi_dns_packet_free(p);
+
+ avahi_log_warn("Record too large, cannot send");
+ job_mark_done(s, rj);
+ return;
+ }
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
+ avahi_interface_send_packet(s->interface, p);
+ avahi_dns_packet_free(p);
+}
+
+static void elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* data) {
+ AvahiResponseJob *rj = data;
+
+ assert(rj);
+
+ if (rj->state == AVAHI_DONE || rj->state == AVAHI_SUPPRESSED)
+ job_free(rj->scheduler, rj); /* Lets drop this entry */
+ else
+ send_response_packet(rj->scheduler, rj);
+}
+
+static AvahiResponseJob* find_scheduled_job(AvahiResponseScheduler *s, AvahiRecord *record) {
+ AvahiResponseJob *rj;
+
+ assert(s);
+ assert(record);
+
+ for (rj = s->jobs; rj; rj = rj->jobs_next) {
+ assert(rj->state == AVAHI_SCHEDULED);
+
+ if (avahi_record_equal_no_ttl(rj->record, record))
+ return rj;
+ }
+
+ return NULL;
+}
+
+static AvahiResponseJob* find_history_job(AvahiResponseScheduler *s, AvahiRecord *record) {
+ AvahiResponseJob *rj;
+
+ assert(s);
+ assert(record);
+
+ for (rj = s->history; rj; rj = rj->jobs_next) {
+ assert(rj->state == AVAHI_DONE);
+
+ if (avahi_record_equal_no_ttl(rj->record, record)) {
+ /* Check whether this entry is outdated */
+
+/* avahi_log_debug("history age: %u", (unsigned) (avahi_age(&rj->delivery)/1000)); */
+
+ if (avahi_age(&rj->delivery)/1000 > AVAHI_RESPONSE_HISTORY_MSEC) {
+ /* it is outdated, so let's remove it */
+ job_free(s, rj);
+ return NULL;
+ }
+
+ return rj;
+ }
+ }
+
+ return NULL;
+}
+
+static AvahiResponseJob* find_suppressed_job(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) {
+ AvahiResponseJob *rj;
+
+ assert(s);
+ assert(record);
+ assert(querier);
+
+ for (rj = s->suppressed; rj; rj = rj->jobs_next) {
+ assert(rj->state == AVAHI_SUPPRESSED);
+ assert(rj->querier_valid);
+
+ if (avahi_record_equal_no_ttl(rj->record, record) &&
+ avahi_address_cmp(&rj->querier, querier) == 0) {
+ /* Check whether this entry is outdated */
+
+ if (avahi_age(&rj->delivery) > AVAHI_RESPONSE_SUPPRESS_MSEC*1000) {
+ /* it is outdated, so let's remove it */
+ job_free(s, rj);
+ return NULL;
+ }
+
+ return rj;
+ }
+ }
+
+ return NULL;
+}
+
+int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) {
+ AvahiResponseJob *rj;
+ struct timeval tv;
+/* char *t; */
+
+ assert(s);
+ assert(record);
+
+ assert(!avahi_key_is_pattern(record->key));
+
+/* t = avahi_record_to_string(record); */
+/* avahi_log_debug("post %i %s", immediately, t); */
+/* avahi_free(t); */
+
+ /* Check whether this response is suppressed */
+ if (querier &&
+ (rj = find_suppressed_job(s, record, querier)) &&
+ avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) &&
+ rj->record->ttl >= record->ttl/2) {
+
+/* avahi_log_debug("Response suppressed by known answer suppression."); */
+ return 0;
+ }
+
+ /* Check if we already sent this response recently */
+ if ((rj = find_history_job(s, record))) {
+
+ if (avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) &&
+ rj->record->ttl >= record->ttl/2 &&
+ (rj->flush_cache || !flush_cache)) {
+/* avahi_log_debug("Response suppressed by local duplicate suppression (history)"); */
+ return 0;
+ }
+
+ /* Outdated ... */
+ job_free(s, rj);
+ }
+
+ avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC);
+
+ if ((rj = find_scheduled_job(s, record))) {
+/* avahi_log_debug("Response suppressed by local duplicate suppression (scheduled)"); */
+
+ /* Update a little ... */
+
+ /* Update the time if the new is prior to the old */
+ if (avahi_timeval_compare(&tv, &rj->delivery) < 0) {
+ rj->delivery = tv;
+ avahi_time_event_update(rj->time_event, &rj->delivery);
+ }
+
+ /* Update the flush cache bit */
+ if (flush_cache)
+ rj->flush_cache = 1;
+
+ /* Update the querier field */
+ if (!querier || (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) != 0))
+ rj->querier_valid = 0;
+
+ /* Update record data (just for the TTL) */
+ avahi_record_unref(rj->record);
+ rj->record = avahi_record_ref(record);
+
+ return 1;
+ } else {
+/* avahi_log_debug("Accepted new response job."); */
+
+ /* Create a new job and schedule it */
+ if (!(rj = job_new(s, record, AVAHI_SCHEDULED)))
+ return 0; /* OOM */
+
+ rj->delivery = tv;
+ rj->time_event = avahi_time_event_new(s->time_event_queue, &rj->delivery, elapse_callback, rj);
+ rj->flush_cache = flush_cache;
+
+ if ((rj->querier_valid = !!querier))
+ rj->querier = *querier;
+
+ return 1;
+ }
+}
+
+void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache) {
+ AvahiResponseJob *rj;
+ assert(s);
+
+ /* This function is called whenever an incoming response was
+ * receieved. We drop scheduled responses which match here. The
+ * keyword is "DUPLICATE ANSWER SUPPRESION". */
+
+ if ((rj = find_scheduled_job(s, record))) {
+
+ if ((!rj->flush_cache || flush_cache) && /* flush cache bit was set correctly */
+ avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */
+ record->ttl >= rj->record->ttl/2) { /* sensible TTL */
+
+ /* A matching entry was found, so let's mark it done */
+/* avahi_log_debug("Response suppressed by distributed duplicate suppression"); */
+ job_mark_done(s, rj);
+ }
+
+ return;
+ }
+
+ if ((rj = find_history_job(s, record))) {
+ /* Found a history job, let's update it */
+ avahi_record_unref(rj->record);
+ rj->record = avahi_record_ref(record);
+ } else
+ /* Found no existing history job, so let's create a new one */
+ if (!(rj = job_new(s, record, AVAHI_DONE)))
+ return; /* OOM */
+
+ rj->flush_cache = flush_cache;
+ rj->querier_valid = 0;
+
+ gettimeofday(&rj->delivery, NULL);
+ job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0);
+}
+
+void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) {
+ AvahiResponseJob *rj;
+
+ assert(s);
+ assert(record);
+ assert(querier);
+
+ if ((rj = find_scheduled_job(s, record))) {
+
+ if (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) == 0 && /* same originator */
+ avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */
+ record->ttl >= rj->record->ttl/2) { /* sensible TTL */
+
+ /* A matching entry was found, so let's drop it */
+/* avahi_log_debug("Known answer suppression active!"); */
+ job_free(s, rj);
+ }
+ }
+
+ if ((rj = find_suppressed_job(s, record, querier))) {
+
+ /* Let's update the old entry */
+ avahi_record_unref(rj->record);
+ rj->record = avahi_record_ref(record);
+
+ } else {
+
+ /* Create a new entry */
+ if (!(rj = job_new(s, record, AVAHI_SUPPRESSED)))
+ return; /* OOM */
+ rj->querier_valid = 1;
+ rj->querier = *querier;
+ }
+
+ gettimeofday(&rj->delivery, NULL);
+ job_set_elapse_time(s, rj, AVAHI_RESPONSE_SUPPRESS_MSEC, 0);
+}
+
+void avahi_response_scheduler_force(AvahiResponseScheduler *s) {
+ assert(s);
+
+ /* Send all scheduled responses immediately */
+ while (s->jobs)
+ send_response_packet(s, s->jobs);
+}
diff --git a/trunk/avahi-core/response-sched.h b/trunk/avahi-core/response-sched.h
new file mode 100644
index 0000000..68c4a9d
--- /dev/null
+++ b/trunk/avahi-core/response-sched.h
@@ -0,0 +1,39 @@
+#ifndef fooresponseschedhfoo
+#define fooresponseschedhfoo
+
+/* $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 AvahiResponseScheduler AvahiResponseScheduler;
+
+#include <avahi-common/address.h>
+#include "iface.h"
+
+AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i);
+void avahi_response_scheduler_free(AvahiResponseScheduler *s);
+void avahi_response_scheduler_clear(AvahiResponseScheduler *s);
+void avahi_response_scheduler_force(AvahiResponseScheduler *s);
+
+int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately);
+void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache);
+void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier);
+
+#endif
diff --git a/trunk/avahi-core/rr-util.h b/trunk/avahi-core/rr-util.h
new file mode 100644
index 0000000..b3c35b4
--- /dev/null
+++ b/trunk/avahi-core/rr-util.h
@@ -0,0 +1,64 @@
+#ifndef foorrutilhfoo
+#define foorrutilhfoo
+
+/* $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 "rr.h"
+
+AVAHI_C_DECL_BEGIN
+
+/** Creaze new AvahiKey object based on an existing key but replaceing the type by CNAME */
+AvahiKey *avahi_key_new_cname(AvahiKey *key);
+
+/** Match a key to a key pattern. The pattern has a type of
+AVAHI_DNS_CLASS_ANY, the classes are taken to be equal. Same for the
+type. If the pattern has neither class nor type with ANY constants,
+this function is identical to avahi_key_equal(). In contrast to
+avahi_equal() this function is not commutative. */
+int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k);
+
+/** Check whether a key is a pattern key, i.e. the class/type has a
+ * value of AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY */
+int avahi_key_is_pattern(const AvahiKey *k);
+
+/** Returns a maximum estimate for the space that is needed to store
+ * this key in a DNS packet. */
+size_t avahi_key_get_estimate_size(AvahiKey *k);
+
+/** Returns a maximum estimate for the space that is needed to store
+ * the record in a DNS packet. */
+size_t avahi_record_get_estimate_size(AvahiRecord *r);
+
+/** Do a mDNS spec conforming lexicographical comparison of the two
+ * records. Return a negative value if a < b, a positive if a > b,
+ * zero if equal. */
+int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b);
+
+/** Return 1 if the specified record is an mDNS goodbye record. i.e. TTL is zero. */
+int avahi_record_is_goodbye(AvahiRecord *r);
+
+/** Make a deep copy of an AvahiRecord object */
+AvahiRecord *avahi_record_copy(AvahiRecord *r);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/rr.c b/trunk/avahi-core/rr.c
new file mode 100644
index 0000000..8b7fab7
--- /dev/null
+++ b/trunk/avahi-core/rr.c
@@ -0,0 +1,703 @@
+/* $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 <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <assert.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/defs.h>
+
+#include "rr.h"
+#include "log.h"
+#include "util.h"
+#include "hashmap.h"
+#include "domain-util.h"
+#include "rr-util.h"
+
+AvahiKey *avahi_key_new(const char *name, uint16_t class, uint16_t type) {
+ AvahiKey *k;
+ assert(name);
+
+ if (!(k = avahi_new(AvahiKey, 1))) {
+ avahi_log_error("avahi_new() failed.");
+ return NULL;
+ }
+
+ if (!(k->name = avahi_normalize_name_strdup(name))) {
+ avahi_log_error("avahi_normalize_name() failed.");
+ avahi_free(k);
+ return NULL;
+ }
+
+ k->ref = 1;
+ k->clazz = class;
+ k->type = 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);
+
+ k->ref++;
+
+ return k;
+}
+
+void avahi_key_unref(AvahiKey *k) {
+ assert(k);
+ assert(k->ref >= 1);
+
+ if ((--k->ref) <= 0) {
+ avahi_free(k->name);
+ avahi_free(k);
+ }
+}
+
+AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl) {
+ AvahiRecord *r;
+
+ assert(k);
+
+ if (!(r = avahi_new(AvahiRecord, 1))) {
+ avahi_log_error("avahi_new() failed.");
+ return NULL;
+ }
+
+ r->ref = 1;
+ r->key = avahi_key_ref(k);
+
+ memset(&r->data, 0, sizeof(r->data));
+
+ r->ttl = ttl != (uint32_t) -1 ? ttl : AVAHI_DEFAULT_TTL;
+
+ return r;
+}
+
+AvahiRecord *avahi_record_new_full(const char *name, uint16_t class, uint16_t type, uint32_t ttl) {
+ AvahiRecord *r;
+ AvahiKey *k;
+
+ assert(name);
+
+ if (!(k = avahi_key_new(name, class, type))) {
+ avahi_log_error("avahi_key_new() failed.");
+ return NULL;
+ }
+
+ r = avahi_record_new(k, ttl);
+ avahi_key_unref(k);
+
+ if (!r) {
+ avahi_log_error("avahi_record_new() failed.");
+ return NULL;
+ }
+
+ return r;
+}
+
+AvahiRecord *avahi_record_ref(AvahiRecord *r) {
+ assert(r);
+ assert(r->ref >= 1);
+
+ r->ref++;
+ return r;
+}
+
+void avahi_record_unref(AvahiRecord *r) {
+ assert(r);
+ assert(r->ref >= 1);
+
+ if ((--r->ref) <= 0) {
+ switch (r->key->type) {
+
+ case AVAHI_DNS_TYPE_SRV:
+ avahi_free(r->data.srv.name);
+ break;
+
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ case AVAHI_DNS_TYPE_NS:
+ avahi_free(r->data.ptr.name);
+ break;
+
+ case AVAHI_DNS_TYPE_HINFO:
+ avahi_free(r->data.hinfo.cpu);
+ avahi_free(r->data.hinfo.os);
+ break;
+
+ case AVAHI_DNS_TYPE_TXT:
+ avahi_string_list_free(r->data.txt.string_list);
+ break;
+
+ case AVAHI_DNS_TYPE_A:
+ case AVAHI_DNS_TYPE_AAAA:
+ break;
+
+ default:
+ avahi_free(r->data.generic.data);
+ }
+
+ avahi_key_unref(r->key);
+ avahi_free(r);
+ }
+}
+
+const char *avahi_dns_class_to_string(uint16_t class) {
+ if (class & AVAHI_DNS_CACHE_FLUSH)
+ return "FLUSH";
+
+ switch (class) {
+ case AVAHI_DNS_CLASS_IN:
+ return "IN";
+ case AVAHI_DNS_CLASS_ANY:
+ return "ANY";
+ default:
+ return NULL;
+ }
+}
+
+const char *avahi_dns_type_to_string(uint16_t type) {
+ switch (type) {
+ case AVAHI_DNS_TYPE_CNAME:
+ return "CNAME";
+ case AVAHI_DNS_TYPE_A:
+ return "A";
+ case AVAHI_DNS_TYPE_AAAA:
+ return "AAAA";
+ case AVAHI_DNS_TYPE_PTR:
+ return "PTR";
+ case AVAHI_DNS_TYPE_HINFO:
+ return "HINFO";
+ case AVAHI_DNS_TYPE_TXT:
+ return "TXT";
+ case AVAHI_DNS_TYPE_SRV:
+ return "SRV";
+ case AVAHI_DNS_TYPE_ANY:
+ return "ANY";
+ case AVAHI_DNS_TYPE_SOA:
+ return "SOA";
+ case AVAHI_DNS_TYPE_NS:
+ return "NS";
+ default:
+ return NULL;
+ }
+}
+
+char *avahi_key_to_string(const AvahiKey *k) {
+ char class[16], type[16];
+ const char *c, *t;
+
+ assert(k);
+ assert(k->ref >= 1);
+
+ /* According to RFC3597 */
+
+ if (!(c = avahi_dns_class_to_string(k->clazz))) {
+ snprintf(class, sizeof(class), "CLASS%u", k->clazz);
+ c = class;
+ }
+
+ if (!(t = avahi_dns_type_to_string(k->type))) {
+ snprintf(type, sizeof(type), "TYPE%u", k->type);
+ t = type;
+ }
+
+ return avahi_strdup_printf("%s\t%s\t%s", k->name, c, t);
+}
+
+char *avahi_record_to_string(const AvahiRecord *r) {
+ char *p, *s;
+ char buf[1024], *t = NULL, *d = NULL;
+
+ assert(r);
+ assert(r->ref >= 1);
+
+ switch (r->key->type) {
+ case AVAHI_DNS_TYPE_A:
+ inet_ntop(AF_INET, &r->data.a.address.address, t = buf, sizeof(buf));
+ break;
+
+ case AVAHI_DNS_TYPE_AAAA:
+ inet_ntop(AF_INET6, &r->data.aaaa.address.address, t = buf, sizeof(buf));
+ break;
+
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ case AVAHI_DNS_TYPE_NS:
+
+ t = r->data.ptr.name;
+ break;
+
+ case AVAHI_DNS_TYPE_TXT:
+ t = d = avahi_string_list_to_string(r->data.txt.string_list);
+ break;
+
+ case AVAHI_DNS_TYPE_HINFO:
+
+ snprintf(t = buf, sizeof(buf), "\"%s\" \"%s\"", r->data.hinfo.cpu, r->data.hinfo.os);
+ break;
+
+ case AVAHI_DNS_TYPE_SRV:
+
+ snprintf(t = buf, sizeof(buf), "%u %u %u %s",
+ r->data.srv.priority,
+ r->data.srv.weight,
+ r->data.srv.port,
+ r->data.srv.name);
+
+ break;
+
+ default: {
+
+ uint8_t *c;
+ uint16_t n;
+ int i;
+ char *e;
+
+ /* According to RFC3597 */
+
+ snprintf(t = buf, sizeof(buf), "\\# %u", r->data.generic.size);
+
+ e = strchr(t, 0);
+
+ for (c = r->data.generic.data, n = r->data.generic.size, i = 0;
+ n > 0 && i < 20;
+ c ++, n --, i++) {
+
+ sprintf(e, " %02X", *c);
+ e = strchr(e, 0);
+ }
+
+ break;
+ }
+ }
+
+ p = avahi_key_to_string(r->key);
+ s = avahi_strdup_printf("%s %s ; ttl=%u", p, t, r->ttl);
+ avahi_free(p);
+ avahi_free(d);
+
+ return s;
+}
+
+int avahi_key_equal(const AvahiKey *a, const AvahiKey *b) {
+ assert(a);
+ assert(b);
+
+ if (a == b)
+ return 1;
+
+ return avahi_domain_equal(a->name, b->name) &&
+ a->type == b->type &&
+ a->clazz == b->clazz;
+}
+
+int avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) {
+ assert(pattern);
+ assert(k);
+
+ assert(!avahi_key_is_pattern(k));
+
+ if (pattern == k)
+ return 1;
+
+ return avahi_domain_equal(pattern->name, k->name) &&
+ (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) &&
+ (pattern->clazz == k->clazz || pattern->clazz == AVAHI_DNS_CLASS_ANY);
+}
+
+int avahi_key_is_pattern(const AvahiKey *k) {
+ assert(k);
+
+ return
+ k->type == AVAHI_DNS_TYPE_ANY ||
+ k->clazz == AVAHI_DNS_CLASS_ANY;
+}
+
+unsigned avahi_key_hash(const AvahiKey *k) {
+ assert(k);
+
+ return
+ avahi_domain_hash(k->name) +
+ k->type +
+ k->clazz;
+}
+
+static int rdata_equal(const AvahiRecord *a, const AvahiRecord *b) {
+ assert(a);
+ assert(b);
+ assert(a->key->type == b->key->type);
+
+ switch (a->key->type) {
+ case AVAHI_DNS_TYPE_SRV:
+ return
+ a->data.srv.priority == b->data.srv.priority &&
+ a->data.srv.weight == b->data.srv.weight &&
+ a->data.srv.port == b->data.srv.port &&
+ avahi_domain_equal(a->data.srv.name, b->data.srv.name);
+
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ case AVAHI_DNS_TYPE_NS:
+ return avahi_domain_equal(a->data.ptr.name, b->data.ptr.name);
+
+ case AVAHI_DNS_TYPE_HINFO:
+ return
+ !strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu) &&
+ !strcmp(a->data.hinfo.os, b->data.hinfo.os);
+
+ case AVAHI_DNS_TYPE_TXT:
+ return avahi_string_list_equal(a->data.txt.string_list, b->data.txt.string_list);
+
+ case AVAHI_DNS_TYPE_A:
+ return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address)) == 0;
+
+ case AVAHI_DNS_TYPE_AAAA:
+ return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address)) == 0;
+
+ default:
+ return a->data.generic.size == b->data.generic.size &&
+ (a->data.generic.size == 0 || memcmp(a->data.generic.data, b->data.generic.data, a->data.generic.size) == 0);
+ }
+
+}
+
+int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) {
+ assert(a);
+ assert(b);
+
+ if (a == b)
+ return 1;
+
+ return
+ avahi_key_equal(a->key, b->key) &&
+ rdata_equal(a, b);
+}
+
+
+AvahiRecord *avahi_record_copy(AvahiRecord *r) {
+ AvahiRecord *copy;
+
+ if (!(copy = avahi_new(AvahiRecord, 1))) {
+ avahi_log_error("avahi_new() failed.");
+ return NULL;
+ }
+
+ copy->ref = 1;
+ copy->key = avahi_key_ref(r->key);
+ copy->ttl = r->ttl;
+
+ switch (r->key->type) {
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ case AVAHI_DNS_TYPE_NS:
+ if (!(copy->data.ptr.name = avahi_strdup(r->data.ptr.name)))
+ goto fail;
+ break;
+
+ case AVAHI_DNS_TYPE_SRV:
+ copy->data.srv.priority = r->data.srv.priority;
+ copy->data.srv.weight = r->data.srv.weight;
+ copy->data.srv.port = r->data.srv.port;
+ if (!(copy->data.srv.name = avahi_strdup(r->data.srv.name)))
+ goto fail;
+ break;
+
+ case AVAHI_DNS_TYPE_HINFO:
+ if (!(copy->data.hinfo.os = avahi_strdup(r->data.hinfo.os)))
+ goto fail;
+
+ if (!(copy->data.hinfo.cpu = avahi_strdup(r->data.hinfo.cpu))) {
+ avahi_free(r->data.hinfo.os);
+ goto fail;
+ }
+ break;
+
+ case AVAHI_DNS_TYPE_TXT:
+ copy->data.txt.string_list = avahi_string_list_copy(r->data.txt.string_list);
+ break;
+
+ case AVAHI_DNS_TYPE_A:
+ copy->data.a.address = r->data.a.address;
+ break;
+
+ case AVAHI_DNS_TYPE_AAAA:
+ copy->data.aaaa.address = r->data.aaaa.address;
+ break;
+
+ default:
+ if (!(copy->data.generic.data = avahi_memdup(r->data.generic.data, r->data.generic.size)))
+ goto fail;
+ copy->data.generic.size = r->data.generic.size;
+ break;
+
+ }
+
+ return copy;
+
+fail:
+ avahi_log_error("Failed to allocate memory");
+
+ avahi_key_unref(copy->key);
+ avahi_free(copy);
+
+ return NULL;
+}
+
+
+size_t avahi_key_get_estimate_size(AvahiKey *k) {
+ assert(k);
+
+ return strlen(k->name)+1+4;
+}
+
+size_t avahi_record_get_estimate_size(AvahiRecord *r) {
+ size_t n;
+ assert(r);
+
+ n = avahi_key_get_estimate_size(r->key) + 4 + 2;
+
+ switch (r->key->type) {
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ case AVAHI_DNS_TYPE_NS:
+ n += strlen(r->data.ptr.name) + 1;
+ break;
+
+ case AVAHI_DNS_TYPE_SRV:
+ n += 6 + strlen(r->data.srv.name) + 1;
+ break;
+
+ case AVAHI_DNS_TYPE_HINFO:
+ n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1;
+ break;
+
+ case AVAHI_DNS_TYPE_TXT:
+ n += avahi_string_list_serialize(r->data.txt.string_list, NULL, 0);
+ break;
+
+ case AVAHI_DNS_TYPE_A:
+ n += sizeof(AvahiIPv4Address);
+ break;
+
+ case AVAHI_DNS_TYPE_AAAA:
+ n += sizeof(AvahiIPv6Address);
+ break;
+
+ default:
+ n += r->data.generic.size;
+ }
+
+ return n;
+}
+
+static int lexicographical_memcmp(const void* a, size_t al, const void* b, size_t bl) {
+ size_t c;
+ int ret;
+
+ assert(a);
+ assert(b);
+
+ c = al < bl ? al : bl;
+ if ((ret = memcmp(a, b, c)))
+ return ret;
+
+ if (al == bl)
+ return 0;
+ else
+ return al == c ? 1 : -1;
+}
+
+static int uint16_cmp(uint16_t a, uint16_t b) {
+ return a == b ? 0 : (a < b ? -1 : 1);
+}
+
+int avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) {
+ int r;
+/* char *t1, *t2; */
+
+ assert(a);
+ assert(b);
+
+/* t1 = avahi_record_to_string(a); */
+/* t2 = avahi_record_to_string(b); */
+/* g_message("lexicocmp: %s %s", t1, t2); */
+/* avahi_free(t1); */
+/* avahi_free(t2); */
+
+ if (a == b)
+ return 0;
+
+ if ((r = uint16_cmp(a->key->clazz, b->key->clazz)) ||
+ (r = uint16_cmp(a->key->type, b->key->type)))
+ return r;
+
+ switch (a->key->type) {
+
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ case AVAHI_DNS_TYPE_NS:
+ return avahi_binary_domain_cmp(a->data.ptr.name, b->data.ptr.name);
+
+ case AVAHI_DNS_TYPE_SRV: {
+ if ((r = uint16_cmp(a->data.srv.priority, b->data.srv.priority)) == 0 &&
+ (r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 &&
+ (r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0)
+ r = avahi_binary_domain_cmp(a->data.srv.name, b->data.srv.name);
+
+ return r;
+ }
+
+ case AVAHI_DNS_TYPE_HINFO: {
+
+ if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) ||
+ (r = strcmp(a->data.hinfo.os, b->data.hinfo.os)))
+ return r;
+
+ return 0;
+
+ }
+
+ case AVAHI_DNS_TYPE_TXT: {
+
+ uint8_t *ma = NULL, *mb = NULL;
+ size_t asize, bsize;
+
+ asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0);
+ bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0);
+
+ if (asize > 0 && !(ma = avahi_new(uint8_t, asize)))
+ goto fail;
+
+ if (bsize > 0 && !(mb = avahi_new(uint8_t, bsize))) {
+ avahi_free(ma);
+ goto fail;
+ }
+
+ avahi_string_list_serialize(a->data.txt.string_list, ma, asize);
+ avahi_string_list_serialize(b->data.txt.string_list, mb, bsize);
+
+ if (asize && bsize)
+ r = lexicographical_memcmp(ma, asize, mb, bsize);
+ else if (asize && !bsize)
+ r = 1;
+ else if (!asize && bsize)
+ r = -1;
+ else
+ r = 0;
+
+ avahi_free(ma);
+ avahi_free(mb);
+
+ return r;
+ }
+
+ case AVAHI_DNS_TYPE_A:
+ return memcmp(&a->data.a.address, &b->data.a.address, sizeof(AvahiIPv4Address));
+
+ case AVAHI_DNS_TYPE_AAAA:
+ return memcmp(&a->data.aaaa.address, &b->data.aaaa.address, sizeof(AvahiIPv6Address));
+
+ default:
+ return lexicographical_memcmp(a->data.generic.data, a->data.generic.size,
+ b->data.generic.data, b->data.generic.size);
+ }
+
+
+fail:
+ avahi_log_error(__FILE__": Out of memory");
+ return -1; /* or whatever ... */
+}
+
+int avahi_record_is_goodbye(AvahiRecord *r) {
+ assert(r);
+
+ return r->ttl == 0;
+}
+
+int avahi_key_is_valid(AvahiKey *k) {
+ assert(k);
+
+ if (!avahi_is_valid_domain_name(k->name))
+ return 0;
+
+ return 1;
+}
+
+int avahi_record_is_valid(AvahiRecord *r) {
+ assert(r);
+
+ if (!avahi_key_is_valid(r->key))
+ return 0;
+
+ switch (r->key->type) {
+
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ case AVAHI_DNS_TYPE_NS:
+ return avahi_is_valid_domain_name(r->data.ptr.name);
+
+ case AVAHI_DNS_TYPE_SRV:
+ return avahi_is_valid_domain_name(r->data.srv.name);
+
+ case AVAHI_DNS_TYPE_HINFO:
+ return
+ strlen(r->data.hinfo.os) <= 255 &&
+ strlen(r->data.hinfo.cpu) <= 255;
+
+ case AVAHI_DNS_TYPE_TXT: {
+
+ AvahiStringList *strlst;
+
+ for (strlst = r->data.txt.string_list; strlst; strlst = strlst->next)
+ if (strlst->size > 255 || strlst->size <= 0)
+ return 0;
+
+ return 1;
+ }
+ }
+
+
+ return 1;
+}
diff --git a/trunk/avahi-core/rr.h b/trunk/avahi-core/rr.h
new file mode 100644
index 0000000..6bfe0ec
--- /dev/null
+++ b/trunk/avahi-core/rr.h
@@ -0,0 +1,174 @@
+#ifndef foorrhfoo
+#define foorrhfoo
+
+/* $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 rr.h Functions and definitions for manipulating DNS resource record (RR) data. */
+
+#include <inttypes.h>
+#include <sys/types.h>
+
+#include <avahi-common/strlst.h>
+#include <avahi-common/address.h>
+#include <avahi-common/cdecl.h>
+
+AVAHI_C_DECL_BEGIN
+
+/** DNS record types, see RFC 1035, in addition to those defined in defs.h */
+enum {
+ AVAHI_DNS_TYPE_ANY = 0xFF, /**< Special query type for requesting all records */
+ AVAHI_DNS_TYPE_OPT = 41, /**< EDNS0 option */
+ AVAHI_DNS_TYPE_TKEY = 249,
+ AVAHI_DNS_TYPE_TSIG = 250,
+ AVAHI_DNS_TYPE_IXFR = 251,
+ AVAHI_DNS_TYPE_AXFR = 252
+};
+
+/** DNS record classes, see RFC 1035, in addition to those defined in defs.h */
+enum {
+ AVAHI_DNS_CLASS_ANY = 0xFF, /**< Special query type for requesting all records */
+ AVAHI_DNS_CACHE_FLUSH = 0x8000, /**< Not really a class but a bit which may be set in response packets, see mDNS spec for more information */
+ AVAHI_DNS_UNICAST_RESPONSE = 0x8000 /**< Not really a class but a bit which may be set in query packets, see mDNS spec for more information */
+};
+
+/** Encapsulates a DNS query key consisting of class, type and
+ name. Use avahi_key_ref()/avahi_key_unref() for manipulating the
+ reference counter. The structure is intended to be treated as "immutable", no
+ changes should be imposed after creation */
+typedef struct AvahiKey {
+ int ref; /**< Reference counter */
+ char *name; /**< Record name */
+ uint16_t clazz; /**< Record class, one of the AVAHI_DNS_CLASS_xxx constants */
+ uint16_t type; /**< Record type, one of the AVAHI_DNS_TYPE_xxx constants */
+} AvahiKey;
+
+/** Encapsulates a DNS resource record. The structure is intended to
+ * be treated as "immutable", no changes should be imposed after
+ * creation. */
+typedef struct AvahiRecord {
+ int ref; /**< Reference counter */
+ AvahiKey *key; /**< Reference to the query key of this record */
+
+ uint32_t ttl; /**< DNS TTL of this record */
+
+ union {
+
+ struct {
+ void* data;
+ uint16_t size;
+ } generic; /**< Generic record data for unknown types */
+
+ struct {
+ uint16_t priority;
+ uint16_t weight;
+ uint16_t port;
+ char *name;
+ } srv; /**< Data for SRV records */
+
+ struct {
+ char *name;
+ } ptr, ns, cname; /**< Data for PTR, NS and CNAME records */
+
+ struct {
+ char *cpu;
+ char *os;
+ } hinfo; /**< Data for HINFO records */
+
+ struct {
+ AvahiStringList *string_list;
+ } txt; /**< Data for TXT records */
+
+ struct {
+ AvahiIPv4Address address;
+ } a; /**< Data for A records */
+
+ struct {
+ AvahiIPv6Address address;
+ } aaaa; /**< Data for AAAA records */
+
+ } data; /**< Record data */
+
+} AvahiRecord;
+
+/** 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);
+
+/** Increase the reference counter of an AvahiKey object by one */
+AvahiKey *avahi_key_ref(AvahiKey *k);
+
+/** Decrease the reference counter of an AvahiKey object by one */
+void avahi_key_unref(AvahiKey *k);
+
+/** Check whether two AvahiKey object contain the same
+ * data. AVAHI_DNS_CLASS_ANY/AVAHI_DNS_TYPE_ANY are treated like any
+ * other class/type. */
+int avahi_key_equal(const AvahiKey *a, const AvahiKey *b);
+
+/** Return a numeric hash value for a key for usage in hash tables. */
+unsigned avahi_key_hash(const AvahiKey *k);
+
+/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */
+AvahiRecord *avahi_record_new(AvahiKey *k, uint32_t ttl);
+
+/** Create a new record object. Record data should be filled in right after creation. The reference counter is set to 1. */
+AvahiRecord *avahi_record_new_full(const char *name, uint16_t clazz, uint16_t type, uint32_t ttl);
+
+/** Increase the reference counter of an AvahiRecord by one. */
+AvahiRecord *avahi_record_ref(AvahiRecord *r);
+
+/** Decrease the reference counter of an AvahiRecord by one. */
+void avahi_record_unref(AvahiRecord *r);
+
+/** Return a textual representation of the specified DNS class. The
+ * returned pointer points to a read only internal string. */
+const char *avahi_dns_class_to_string(uint16_t clazz);
+
+/** Return a textual representation of the specified DNS class. The
+ * returned pointer points to a read only internal string. */
+const char *avahi_dns_type_to_string(uint16_t type);
+
+/** Create a textual representation of the specified key. avahi_free() the
+ * result! */
+char *avahi_key_to_string(const AvahiKey *k);
+
+/** Create a textual representation of the specified record, similar
+ * in style to BIND zone file data. avahi_free() the result! */
+char *avahi_record_to_string(const AvahiRecord *r);
+
+/** Check whether two records are equal (regardless of the TTL */
+int avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b);
+
+/** Check whether the specified key is valid */
+int avahi_key_is_valid(AvahiKey *k);
+
+/** Check whether the specified record is valid */
+int avahi_record_is_valid(AvahiRecord *r);
+
+/** Parse a binary rdata object and fill it into *record. This function is actually implemented in dns.c */
+int avahi_rdata_parse(AvahiRecord *record, const void* rdata, size_t size);
+
+/** Serialize an AvahiRecord object into binary rdata. This function is actually implemented in dns.c */
+size_t avahi_rdata_serialize(AvahiRecord *record, void *rdata, size_t max_size);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/rrlist.c b/trunk/avahi-core/rrlist.c
new file mode 100644
index 0000000..915ecbb
--- /dev/null
+++ b/trunk/avahi-core/rrlist.c
@@ -0,0 +1,190 @@
+/* $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/llist.h>
+#include <avahi-common/malloc.h>
+
+#include "rrlist.h"
+#include "log.h"
+
+typedef struct AvahiRecordListItem AvahiRecordListItem;
+
+struct AvahiRecordListItem {
+ int read;
+ AvahiRecord *record;
+ int unicast_response;
+ int flush_cache;
+ int auxiliary;
+ AVAHI_LLIST_FIELDS(AvahiRecordListItem, items);
+};
+
+struct AvahiRecordList {
+ AVAHI_LLIST_HEAD(AvahiRecordListItem, read);
+ AVAHI_LLIST_HEAD(AvahiRecordListItem, unread);
+
+ int all_flush_cache;
+};
+
+AvahiRecordList *avahi_record_list_new(void) {
+ AvahiRecordList *l;
+
+ if (!(l = avahi_new(AvahiRecordList, 1))) {
+ avahi_log_error("avahi_new() failed.");
+ return NULL;
+ }
+
+ AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->read);
+ AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->unread);
+
+ l->all_flush_cache = 1;
+ return l;
+}
+
+void avahi_record_list_free(AvahiRecordList *l) {
+ assert(l);
+
+ avahi_record_list_flush(l);
+ avahi_free(l);
+}
+
+static void item_free(AvahiRecordList *l, AvahiRecordListItem *i) {
+ assert(l);
+ assert(i);
+
+ if (i->read)
+ AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->read, i);
+ else
+ AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i);
+
+ avahi_record_unref(i->record);
+ avahi_free(i);
+}
+
+void avahi_record_list_flush(AvahiRecordList *l) {
+ assert(l);
+
+ while (l->read)
+ item_free(l, l->read);
+ while (l->unread)
+ item_free(l, l->unread);
+
+ l->all_flush_cache = 1;
+}
+
+AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary) {
+ AvahiRecord *r;
+ AvahiRecordListItem *i;
+
+ if (!(i = l->unread))
+ return NULL;
+
+ assert(!i->read);
+
+ r = avahi_record_ref(i->record);
+ if (ret_unicast_response)
+ *ret_unicast_response = i->unicast_response;
+ if (ret_flush_cache)
+ *ret_flush_cache = i->flush_cache;
+ if (ret_auxiliary)
+ *ret_auxiliary = i->auxiliary;
+
+ AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i);
+ AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->read, i);
+
+ i->read = 1;
+
+ return r;
+}
+
+static AvahiRecordListItem *get(AvahiRecordList *l, AvahiRecord *r) {
+ AvahiRecordListItem *i;
+
+ assert(l);
+ assert(r);
+
+ for (i = l->read; i; i = i->items_next)
+ if (avahi_record_equal_no_ttl(i->record, r))
+ return i;
+
+ for (i = l->unread; i; i = i->items_next)
+ if (avahi_record_equal_no_ttl(i->record, r))
+ return i;
+
+ return NULL;
+}
+
+void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary) {
+ AvahiRecordListItem *i;
+
+ assert(l);
+ assert(r);
+
+ if (get(l, r))
+ return;
+
+ if (!(i = avahi_new(AvahiRecordListItem, 1))) {
+ avahi_log_error("avahi_new() failed.");
+ return;
+ }
+
+ i->unicast_response = unicast_response;
+ i->flush_cache = flush_cache;
+ i->auxiliary = auxiliary;
+ i->record = avahi_record_ref(r);
+ i->read = 0;
+
+ l->all_flush_cache = l->all_flush_cache && flush_cache;
+
+ AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->unread, i);
+}
+
+void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r) {
+ AvahiRecordListItem *i;
+
+ assert(l);
+ assert(r);
+
+ if (!(i = get(l, r)))
+ return;
+
+ item_free(l, i);
+}
+
+int avahi_record_list_is_empty(AvahiRecordList *l) {
+ assert(l);
+
+ return !l->unread && !l->read;
+}
+
+int avahi_record_list_all_flush_cache(AvahiRecordList *l) {
+ assert(l);
+
+ /* Return TRUE if all entries in this list have flush_cache set */
+
+ return l->all_flush_cache;
+}
diff --git a/trunk/avahi-core/rrlist.h b/trunk/avahi-core/rrlist.h
new file mode 100644
index 0000000..9c07ecd
--- /dev/null
+++ b/trunk/avahi-core/rrlist.h
@@ -0,0 +1,42 @@
+#ifndef foorrlisthfoo
+#define foorrlisthfoo
+
+/* $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 "rr.h"
+
+typedef struct AvahiRecordList AvahiRecordList;
+
+AvahiRecordList *avahi_record_list_new(void);
+void avahi_record_list_free(AvahiRecordList *l);
+void avahi_record_list_flush(AvahiRecordList *l);
+
+AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary);
+void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary);
+void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r);
+
+int avahi_record_list_all_flush_cache(AvahiRecordList *l);
+
+int avahi_record_list_is_empty(AvahiRecordList *l);
+
+#endif
diff --git a/trunk/avahi-core/server.c b/trunk/avahi-core/server.c
new file mode 100644
index 0000000..5bfbc50
--- /dev/null
+++ b/trunk/avahi-core/server.c
@@ -0,0 +1,1723 @@
+/* $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 <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include <avahi-common/domain.h>
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+
+#include "internal.h"
+#include "iface.h"
+#include "socket.h"
+#include "browse.h"
+#include "log.h"
+#include "util.h"
+#include "dns-srv-rr.h"
+#include "addr-util.h"
+#include "domain-util.h"
+#include "rr-util.h"
+
+static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const char *name, uint16_t type, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
+ assert(s);
+ assert(i);
+ assert(name);
+ assert(callback);
+
+ if (type == AVAHI_DNS_TYPE_ANY) {
+ AvahiEntry *e;
+
+ for (e = s->entries; e; e = e->entries_next)
+ if (!e->dead &&
+ avahi_entry_is_registered(s, e, i) &&
+ e->record->key->clazz == AVAHI_DNS_CLASS_IN &&
+ avahi_domain_equal(name, e->record->key->name))
+ callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
+
+ } else {
+ AvahiEntry *e;
+ AvahiKey *k;
+
+ if (!(k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type)))
+ return; /** OOM */
+
+ for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
+ if (!e->dead && avahi_entry_is_registered(s, e, i))
+ callback(s, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, userdata);
+
+ avahi_key_unref(k);
+ }
+}
+
+void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata), void* userdata) {
+ assert(s);
+ assert(i);
+ assert(r);
+ assert(callback);
+
+ /* Call the specified callback far all records referenced by the one specified in *r */
+
+ if (r->key->clazz == AVAHI_DNS_CLASS_IN) {
+ if (r->key->type == AVAHI_DNS_TYPE_PTR) {
+ enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
+ enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
+ } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
+ enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
+ enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
+ } else if (r->key->type == AVAHI_DNS_TYPE_CNAME)
+ enum_aux_records(s, i, r->data.cname.name, AVAHI_DNS_TYPE_ANY, callback, userdata);
+ }
+}
+
+void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, int unicast_response, int auxiliary) {
+ assert(s);
+ assert(i);
+ assert(e);
+
+ avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_PUBLISH_UNIQUE, unicast_response, auxiliary);
+}
+
+void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, int unicast_response) {
+ assert(s);
+ assert(i);
+ assert(k);
+
+ /* Push all records that match the specified key to the record list */
+
+ if (avahi_key_is_pattern(k)) {
+ AvahiEntry *e;
+
+ /* Handle ANY query */
+
+ for (e = s->entries; e; e = e->entries_next)
+ if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_is_registered(s, e, i))
+ avahi_server_prepare_response(s, i, e, unicast_response, 0);
+
+ } else {
+ AvahiEntry *e;
+
+ /* Handle all other queries */
+
+ for (e = avahi_hashmap_lookup(s->entries_by_key, k); e; e = e->by_key_next)
+ if (!e->dead && avahi_entry_is_registered(s, e, i))
+ avahi_server_prepare_response(s, i, e, unicast_response, 0);
+ }
+
+ /* Look for CNAME records */
+
+ if ((k->clazz == AVAHI_DNS_CLASS_IN || k->clazz == AVAHI_DNS_CLASS_ANY)
+ && k->type != AVAHI_DNS_TYPE_CNAME && k->type != AVAHI_DNS_TYPE_ANY) {
+
+ AvahiKey *cname_key;
+
+ if (!(cname_key = avahi_key_new(k->name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_CNAME)))
+ return;
+
+ avahi_server_prepare_matching_responses(s, i, cname_key, unicast_response);
+ avahi_key_unref(cname_key);
+ }
+}
+
+static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
+ assert(s);
+ assert(e);
+
+ /* Withdraw the specified entry, and if is part of an entry group,
+ * put that into COLLISION state */
+
+ if (e->dead)
+ return;
+
+ if (e->group) {
+ AvahiEntry *k;
+
+ for (k = e->group->entries; k; k = k->by_group_next)
+ if (!k->dead) {
+ avahi_goodbye_entry(s, k, 0, 1);
+ k->dead = 1;
+ }
+
+ e->group->n_probing = 0;
+
+ avahi_s_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
+ } else {
+ avahi_goodbye_entry(s, e, 0, 1);
+ e->dead = 1;
+ }
+
+ s->need_entry_cleanup = 1;
+}
+
+static void withdraw_rrset(AvahiServer *s, AvahiKey *key) {
+ AvahiEntry *e;
+
+ assert(s);
+ assert(key);
+
+ /* Withdraw an entry RRSset */
+
+ for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
+ withdraw_entry(s, e);
+}
+
+static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
+ AvahiEntry *e, *n;
+ int ours = 0, won = 0, lost = 0;
+
+ assert(s);
+ assert(record);
+ assert(i);
+
+ /* Handle incoming probes and check if they conflict our own probes */
+
+ for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
+ int cmp;
+ n = e->by_key_next;
+
+ if (e->dead)
+ continue;
+
+ if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) {
+ ours = 1;
+ break;
+ } else {
+
+ if (avahi_entry_is_probing(s, e, i)) {
+ if (cmp > 0)
+ won = 1;
+ else /* cmp < 0 */
+ lost = 1;
+ }
+ }
+ }
+
+ if (!ours) {
+ char *t = avahi_record_to_string(record);
+
+ if (won)
+ avahi_log_debug("Recieved conflicting probe [%s]. Local host won.", t);
+ else if (lost) {
+ avahi_log_debug("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
+ withdraw_rrset(s, record->key);
+ }
+
+ avahi_free(t);
+ }
+}
+
+static int handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, int unique) {
+ int valid = 1, ours = 0, conflict = 0, withdraw_immediately = 0;
+ AvahiEntry *e, *n, *conflicting_entry = NULL;
+
+ assert(s);
+ assert(i);
+ assert(record);
+
+ /* Check whether an incoming record conflicts with one of our own */
+
+ for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = n) {
+ n = e->by_key_next;
+
+ if (e->dead)
+ continue;
+
+ /* Check if the incoming is a goodbye record */
+ if (avahi_record_is_goodbye(record)) {
+
+ if (avahi_record_equal_no_ttl(e->record, record)) {
+ char *t;
+
+ /* Refresh */
+ t = avahi_record_to_string(record);
+ avahi_log_debug("Recieved goodbye record for one of our records [%s]. Refreshing.", t);
+ avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
+
+ valid = 0;
+ avahi_free(t);
+ break;
+ }
+
+ /* If the goodybe packet doesn't match one of our own RRs, we simply ignore it. */
+ continue;
+ }
+
+ if (!(e->flags & AVAHI_PUBLISH_UNIQUE) && !unique)
+ continue;
+
+ /* Either our entry or the other is intended to be unique, so let's check */
+
+ if (avahi_record_equal_no_ttl(e->record, record)) {
+ ours = 1; /* We have an identical record, so this is no conflict */
+
+ /* Check wheter there is a TTL conflict */
+ if (record->ttl <= e->record->ttl/2 &&
+ avahi_entry_is_registered(s, e, i)) {
+ char *t;
+ /* Refresh */
+ t = avahi_record_to_string(record);
+
+ avahi_log_debug("Recieved record with bad TTL [%s]. Refreshing.", t);
+ avahi_server_prepare_matching_responses(s, i, e->record->key, 0);
+ valid = 0;
+
+ avahi_free(t);
+ }
+
+ /* There's no need to check the other entries of this RRset */
+ break;
+
+ } else {
+
+ if (avahi_entry_is_registered(s, e, i)) {
+
+ /* A conflict => we have to return to probe mode */
+ conflict = 1;
+ conflicting_entry = e;
+
+ } else if (avahi_entry_is_probing(s, e, i)) {
+
+ /* We are currently registering a matching record, but
+ * someone else already claimed it, so let's
+ * withdraw */
+ conflict = 1;
+ withdraw_immediately = 1;
+ }
+ }
+ }
+
+ if (!ours && conflict) {
+ char *t;
+
+ valid = 0;
+
+ t = avahi_record_to_string(record);
+
+ if (withdraw_immediately) {
+ avahi_log_debug("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
+ withdraw_rrset(s, record->key);
+ } else {
+ assert(conflicting_entry);
+ avahi_log_debug("Recieved conflicting record [%s]. Resetting our record.", t);
+ avahi_entry_return_to_initial_state(s, conflicting_entry, i);
+
+ /* Local unique records are returned to probing
+ * state. Local shared records are reannounced. */
+ }
+
+ avahi_free(t);
+ }
+
+ return valid;
+}
+
+static void append_aux_callback(AvahiServer *s, AvahiRecord *r, int flush_cache, void* userdata) {
+ int *unicast_response = userdata;
+
+ assert(s);
+ assert(r);
+ assert(unicast_response);
+
+ avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, 1);
+}
+
+static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int unicast_response) {
+ assert(s);
+ assert(r);
+
+ avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
+}
+
+void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port, int legacy_unicast, int immediately) {
+
+ assert(s);
+ assert(i);
+ assert(!legacy_unicast || (a && port > 0 && p));
+
+ if (legacy_unicast) {
+ AvahiDnsPacket *reply;
+ AvahiRecord *r;
+
+ if (!(reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , 1, 1)))
+ return; /* OOM */
+
+ while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
+
+ append_aux_records_to_list(s, i, r, 0);
+
+ if (avahi_dns_packet_append_record(reply, r, 0, 10))
+ avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
+ else {
+ char *t = avahi_record_to_string(r);
+ avahi_log_warn("Record [%s] not fitting in legacy unicast packet, dropping.", t);
+ avahi_free(t);
+ }
+
+ avahi_record_unref(r);
+ }
+
+ if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
+ avahi_interface_send_packet_unicast(i, reply, a, port);
+
+ avahi_dns_packet_free(reply);
+
+ } else {
+ int unicast_response, flush_cache, auxiliary;
+ AvahiDnsPacket *reply = NULL;
+ AvahiRecord *r;
+
+ /* In case the query packet was truncated never respond
+ immediately, because known answer suppression records might be
+ contained in later packets */
+ int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
+
+ while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
+
+ int im = immediately;
+
+ /* Only send the response immediately if it contains a
+ * unique entry AND it is not in reply to a truncated
+ * packet AND it is not an auxiliary record AND all other
+ * responses for this record are unique too. */
+
+ if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list))
+ im = 1;
+
+ if (!avahi_interface_post_response(i, r, flush_cache, a, im) && unicast_response) {
+
+ /* Due to some reasons the record has not been scheduled.
+ * The client requested an unicast response in that
+ * case. Therefore we prepare such a response */
+
+ append_aux_records_to_list(s, i, r, unicast_response);
+
+ for (;;) {
+
+ if (!reply) {
+ assert(p);
+
+ if (!(reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, 0, 0)))
+ break; /* OOM */
+ }
+
+ if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
+
+ /* Appending this record succeeded, so incremeant
+ * the specific header field, and return to the caller */
+
+ avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
+
+ break;
+ }
+
+ if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
+ size_t size;
+
+ /* The record is too large for one packet, so create a larger packet */
+
+ avahi_dns_packet_free(reply);
+ size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
+ if (size > AVAHI_DNS_PACKET_SIZE_MAX)
+ size = AVAHI_DNS_PACKET_SIZE_MAX;
+
+ if (!(reply = avahi_dns_packet_new_reply(p, size, 0, 1)))
+ break; /* OOM */
+
+ if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
+ char *t;
+ avahi_dns_packet_free(reply);
+ t = avahi_record_to_string(r);
+ avahi_log_warn("Record [%s] too large, doesn't fit in any packet!", t);
+ avahi_free(t);
+ break;
+ } else
+ avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
+ }
+
+ /* Appending the record didn't succeeed, so let's send this packet, and create a new one */
+ avahi_interface_send_packet_unicast(i, reply, a, port);
+ avahi_dns_packet_free(reply);
+ reply = NULL;
+ }
+ }
+
+ avahi_record_unref(r);
+ }
+
+ if (reply) {
+ if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) != 0)
+ avahi_interface_send_packet_unicast(i, reply, a, port);
+ avahi_dns_packet_free(reply);
+ }
+ }
+
+ avahi_record_list_flush(s->record_list);
+}
+
+static void reflect_response(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, int flush_cache) {
+ AvahiInterface *j;
+
+ assert(s);
+ assert(i);
+ assert(r);
+
+ if (!s->config.enable_reflector)
+ return;
+
+ for (j = s->monitor->interfaces; j; j = j->interface_next)
+ if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
+ avahi_interface_post_response(j, r, flush_cache, NULL, 1);
+}
+
+static void* reflect_cache_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) {
+ AvahiServer *s = userdata;
+
+ assert(c);
+ assert(pattern);
+ assert(e);
+ assert(s);
+
+ avahi_record_list_push(s->record_list, e->record, e->cache_flush, 0, 0);
+ return NULL;
+}
+
+static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
+ AvahiInterface *j;
+
+ assert(s);
+ assert(i);
+ assert(k);
+
+ if (!s->config.enable_reflector)
+ return;
+
+ for (j = s->monitor->interfaces; j; j = j->interface_next)
+ if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
+ /* Post the query to other networks */
+ avahi_interface_post_query(j, k, 1, NULL);
+
+ /* Reply from caches of other network. This is needed to
+ * "work around" known answer suppression. */
+
+ avahi_cache_walk(j->cache, k, reflect_cache_walk_callback, s);
+ }
+}
+
+static void reflect_probe(AvahiServer *s, AvahiInterface *i, AvahiRecord *r) {
+ AvahiInterface *j;
+
+ assert(s);
+ assert(i);
+ assert(r);
+
+ if (!s->config.enable_reflector)
+ return;
+
+ for (j = s->monitor->interfaces; j; j = j->interface_next)
+ if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol))
+ avahi_interface_post_probe(j, r, 1);
+}
+
+static void handle_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port, int legacy_unicast, int from_local_iface) {
+ size_t n;
+ int is_probe;
+
+ assert(s);
+ assert(p);
+ assert(i);
+ assert(a);
+
+ assert(avahi_record_list_is_empty(s->record_list));
+
+ is_probe = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) > 0;
+
+ /* Handle the questions */
+ for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
+ AvahiKey *key;
+ int unicast_response = 0;
+
+ if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
+ avahi_log_warn(__FILE__": Packet too short or invalid while reading question key. (Maybe an UTF8 problem?)");
+ goto fail;
+ }
+
+ if (!legacy_unicast && !from_local_iface) {
+ reflect_query(s, i, key);
+ avahi_cache_start_poof(i->cache, key, a);
+ }
+
+ if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 &&
+ !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC))
+ /* Allow our own queries to be suppressed by incoming
+ * queries only when they do not include known answers */
+ avahi_query_scheduler_incoming(i->query_scheduler, key);
+
+ avahi_server_prepare_matching_responses(s, i, key, unicast_response);
+ avahi_key_unref(key);
+ }
+
+ if (!legacy_unicast) {
+
+ /* Known Answer Suppression */
+ for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
+ AvahiRecord *record;
+ int unique = 0;
+
+ if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
+ avahi_log_warn(__FILE__": Packet too short or invalid while reading known answer record. (Maybe an UTF8 problem?)");
+ goto fail;
+ }
+
+ if (handle_conflict(s, i, record, unique)) {
+ avahi_response_scheduler_suppress(i->response_scheduler, record, a);
+ avahi_record_list_drop(s->record_list, record);
+ avahi_cache_stop_poof(i->cache, record, a);
+ }
+
+ avahi_record_unref(record);
+ }
+
+ /* Probe record */
+ for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT); n > 0; n --) {
+ AvahiRecord *record;
+ int unique = 0;
+
+ if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
+ avahi_log_warn(__FILE__": Packet too short or invalid while reading probe record. (Maybe an UTF8 problem?)");
+ goto fail;
+ }
+
+ if (!avahi_key_is_pattern(record->key)) {
+ if (!from_local_iface)
+ reflect_probe(s, i, record);
+ incoming_probe(s, record, i);
+ }
+
+ avahi_record_unref(record);
+ }
+ }
+
+ if (!avahi_record_list_is_empty(s->record_list))
+ avahi_server_generate_response(s, i, p, a, port, legacy_unicast, is_probe);
+
+ return;
+
+fail:
+ avahi_record_list_flush(s->record_list);
+}
+
+static void handle_response_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, int from_local_iface) {
+ unsigned n;
+
+ assert(s);
+ assert(p);
+ assert(i);
+ assert(a);
+
+ for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) +
+ avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT); n > 0; n--) {
+ AvahiRecord *record;
+ int cache_flush = 0;
+/* char *txt; */
+
+ if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
+ avahi_log_warn(__FILE__": Packet too short or invalid while reading response record. (Maybe an UTF8 problem?)");
+ break;
+ }
+
+ if (!avahi_key_is_pattern(record->key)) {
+
+ if (handle_conflict(s, i, record, cache_flush)) {
+ if (!from_local_iface)
+ reflect_response(s, i, record, cache_flush);
+ avahi_cache_update(i->cache, record, cache_flush, a);
+ avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
+ }
+ }
+
+ avahi_record_unref(record);
+ }
+
+ /* If the incoming response contained a conflicting record, some
+ records have been scheduling for sending. We need to flush them
+ here. */
+ if (!avahi_record_list_is_empty(s->record_list))
+ avahi_server_generate_response(s, i, NULL, NULL, 0, 0, 1);
+}
+
+static AvahiLegacyUnicastReflectSlot* allocate_slot(AvahiServer *s) {
+ unsigned n, idx = (unsigned) -1;
+ AvahiLegacyUnicastReflectSlot *slot;
+
+ assert(s);
+
+ if (!s->legacy_unicast_reflect_slots)
+ s->legacy_unicast_reflect_slots = avahi_new0(AvahiLegacyUnicastReflectSlot*, AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX);
+
+ for (n = 0; n < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; n++, s->legacy_unicast_reflect_id++) {
+ idx = s->legacy_unicast_reflect_id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
+
+ if (!s->legacy_unicast_reflect_slots[idx])
+ break;
+ }
+
+ if (idx == (unsigned) -1 || s->legacy_unicast_reflect_slots[idx])
+ return NULL;
+
+ if (!(slot = avahi_new(AvahiLegacyUnicastReflectSlot, 1)))
+ return NULL; /* OOM */
+
+ s->legacy_unicast_reflect_slots[idx] = slot;
+ slot->id = s->legacy_unicast_reflect_id++;
+ slot->server = s;
+
+ return slot;
+}
+
+static void deallocate_slot(AvahiServer *s, AvahiLegacyUnicastReflectSlot *slot) {
+ unsigned idx;
+
+ assert(s);
+ assert(slot);
+
+ idx = slot->id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
+
+ assert(s->legacy_unicast_reflect_slots[idx] == slot);
+
+ avahi_time_event_free(slot->time_event);
+
+ avahi_free(slot);
+ s->legacy_unicast_reflect_slots[idx] = NULL;
+}
+
+static void free_slots(AvahiServer *s) {
+ unsigned idx;
+ assert(s);
+
+ if (!s->legacy_unicast_reflect_slots)
+ return;
+
+ for (idx = 0; idx < AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX; idx ++)
+ if (s->legacy_unicast_reflect_slots[idx])
+ deallocate_slot(s, s->legacy_unicast_reflect_slots[idx]);
+
+ avahi_free(s->legacy_unicast_reflect_slots);
+ s->legacy_unicast_reflect_slots = NULL;
+}
+
+static AvahiLegacyUnicastReflectSlot* find_slot(AvahiServer *s, uint16_t id) {
+ unsigned idx;
+
+ assert(s);
+
+ if (!s->legacy_unicast_reflect_slots)
+ return NULL;
+
+ idx = id % AVAHI_LEGACY_UNICAST_REFLECT_SLOTS_MAX;
+
+ if (!s->legacy_unicast_reflect_slots[idx] || s->legacy_unicast_reflect_slots[idx]->id != id)
+ return NULL;
+
+ return s->legacy_unicast_reflect_slots[idx];
+}
+
+static void legacy_unicast_reflect_slot_timeout(AvahiTimeEvent *e, void *userdata) {
+ AvahiLegacyUnicastReflectSlot *slot = userdata;
+
+ assert(e);
+ assert(slot);
+ assert(slot->time_event == e);
+
+ deallocate_slot(slot->server, slot);
+}
+
+static void reflect_legacy_unicast_query_packet(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, uint16_t port) {
+ AvahiLegacyUnicastReflectSlot *slot;
+ AvahiInterface *j;
+
+ assert(s);
+ assert(p);
+ assert(i);
+ assert(a);
+ assert(port > 0);
+ assert(i->protocol == a->proto);
+
+ if (!s->config.enable_reflector)
+ return;
+
+ /* Reflecting legacy unicast queries is a little more complicated
+ than reflecting normal queries, since we must route the
+ responses back to the right client. Therefore we must store
+ some information for finding the right client contact data for
+ response packets. In contrast to normal queries legacy
+ unicast query and response packets are reflected untouched and
+ are not reassembled into larger packets */
+
+ if (!(slot = allocate_slot(s))) {
+ /* No slot available, we drop this legacy unicast query */
+ avahi_log_warn("No slot available for legacy unicast reflection, dropping query packet.");
+ return;
+ }
+
+ slot->original_id = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID);
+ slot->address = *a;
+ slot->port = port;
+ slot->interface = i->hardware->index;
+
+ avahi_elapse_time(&slot->elapse_time, 2000, 0);
+ slot->time_event = avahi_time_event_new(s->time_event_queue, &slot->elapse_time, legacy_unicast_reflect_slot_timeout, slot);
+
+ /* Patch the packet with our new locally generatet id */
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
+
+ for (j = s->monitor->interfaces; j; j = j->interface_next)
+ if (avahi_interface_is_relevant(j) &&
+ j != i &&
+ (s->config.reflect_ipv || j->protocol == i->protocol)) {
+
+ if (j->protocol == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
+ avahi_send_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, j->hardware->index, p, NULL, NULL, 0);
+ } else if (j->protocol == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0)
+ avahi_send_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, j->hardware->index, p, NULL, NULL, 0);
+ }
+
+ /* Reset the id */
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
+}
+
+static int originates_from_local_legacy_unicast_socket(AvahiServer *s, const AvahiAddress *address, uint16_t port) {
+ assert(s);
+ assert(address);
+ assert(port > 0);
+
+ if (!s->config.enable_reflector)
+ return 0;
+
+ if (!avahi_address_is_local(s->monitor, address))
+ return 0;
+
+ if (address->proto == AVAHI_PROTO_INET && s->fd_legacy_unicast_ipv4 >= 0) {
+ struct sockaddr_in lsa;
+ socklen_t l = sizeof(lsa);
+
+ if (getsockname(s->fd_legacy_unicast_ipv4, (struct sockaddr*) &lsa, &l) != 0)
+ avahi_log_warn("getsockname(): %s", strerror(errno));
+ else
+ return lsa.sin_port == port;
+
+ }
+
+ if (address->proto == AVAHI_PROTO_INET6 && s->fd_legacy_unicast_ipv6 >= 0) {
+ struct sockaddr_in6 lsa;
+ socklen_t l = sizeof(lsa);
+
+ if (getsockname(s->fd_legacy_unicast_ipv6, (struct sockaddr*) &lsa, &l) != 0)
+ avahi_log_warn("getsockname(): %s", strerror(errno));
+ else
+ return lsa.sin6_port == port;
+ }
+
+ return 0;
+}
+
+static int is_mdns_mcast_address(const AvahiAddress *a) {
+ AvahiAddress b;
+ assert(a);
+
+ avahi_address_parse(a->proto == AVAHI_PROTO_INET ? AVAHI_IPV4_MCAST_GROUP : AVAHI_IPV6_MCAST_GROUP, a->proto, &b);
+ return avahi_address_cmp(a, &b) == 0;
+}
+
+static int originates_from_local_iface(AvahiServer *s, AvahiIfIndex iface, const AvahiAddress *a, uint16_t port) {
+ assert(s);
+ assert(iface != AVAHI_IF_UNSPEC);
+ assert(a);
+
+ /* If it isn't the MDNS port it can't be generated by us */
+ if (port != AVAHI_MDNS_PORT)
+ return 0;
+
+ return avahi_interface_has_address(s->monitor, iface, a);
+}
+
+static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, const AvahiAddress *src_address, uint16_t port, const AvahiAddress *dst_address, AvahiIfIndex iface, int ttl) {
+ AvahiInterface *i;
+ int from_local_iface = 0;
+
+ assert(s);
+ assert(p);
+ assert(src_address);
+ assert(dst_address);
+ assert(iface > 0);
+ assert(src_address->proto == dst_address->proto);
+
+ if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, src_address->proto)) ||
+ !avahi_interface_is_relevant(i)) {
+ avahi_log_warn("Recieved packet from invalid interface.");
+ return;
+ }
+
+ if (avahi_address_is_ipv4_in_ipv6(src_address))
+ /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
+ return;
+
+ if (originates_from_local_legacy_unicast_socket(s, src_address, port))
+ /* This originates from our local reflector, so let's ignore it */
+ return;
+
+ /* We don't want to reflect local traffic, so we check if this packet is generated locally. */
+ if (s->config.enable_reflector)
+ from_local_iface = originates_from_local_iface(s, iface, src_address, port);
+
+ if (avahi_dns_packet_check_valid_multicast(p) < 0) {
+ avahi_log_warn("Recieved invalid packet.");
+ return;
+ }
+
+ if (avahi_dns_packet_is_query(p)) {
+ int legacy_unicast = 0;
+
+ if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
+ avahi_log_warn("Invalid query packet.");
+ return;
+ }
+
+ if (port != AVAHI_MDNS_PORT) {
+ /* Legacy Unicast */
+
+ if ((avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) != 0 ||
+ avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0)) {
+ avahi_log_warn("Invalid legacy unicast query packet.");
+ return;
+ }
+
+ legacy_unicast = 1;
+ }
+
+ if (legacy_unicast)
+ reflect_legacy_unicast_query_packet(s, p, i, src_address, port);
+
+ handle_query_packet(s, p, i, src_address, port, legacy_unicast, from_local_iface);
+
+ } else {
+ if (port != AVAHI_MDNS_PORT) {
+ avahi_log_warn("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
+ return;
+ }
+
+ if (ttl != 255 && s->config.check_response_ttl) {
+ avahi_log_warn("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
+ return;
+ }
+
+ if (!is_mdns_mcast_address(dst_address) &&
+ !avahi_interface_address_on_link(i, src_address)) {
+ avahi_log_warn("Received non-local response on interface '%s.%i'.", i->hardware->name, i->protocol);
+ return;
+ }
+
+ if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) != 0 ||
+ avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0 ||
+ avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_NSCOUNT) != 0) {
+ avahi_log_warn("Invalid response packet.");
+ return;
+ }
+
+ handle_response_packet(s, p, i, src_address, from_local_iface);
+ }
+}
+
+static void dispatch_legacy_unicast_packet(AvahiServer *s, AvahiDnsPacket *p) {
+ AvahiInterface *j;
+ AvahiLegacyUnicastReflectSlot *slot;
+
+ assert(s);
+ assert(p);
+
+ if (avahi_dns_packet_check_valid(p) < 0 || avahi_dns_packet_is_query(p)) {
+ avahi_log_warn("Recieved invalid packet.");
+ return;
+ }
+
+ if (!(slot = find_slot(s, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID)))) {
+ avahi_log_warn("Recieved legacy unicast response with unknown id");
+ return;
+ }
+
+ if (!(j = avahi_interface_monitor_get_interface(s->monitor, slot->interface, slot->address.proto)) ||
+ !avahi_interface_is_relevant(j))
+ return;
+
+ /* Patch the original ID into this response */
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->original_id);
+
+ /* Forward the response to the correct client */
+ avahi_interface_send_packet_unicast(j, p, &slot->address, slot->port);
+
+ /* Undo changes to packet */
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ID, slot->id);
+}
+
+static void cleanup_dead(AvahiServer *s) {
+ assert(s);
+
+ avahi_cleanup_dead_entries(s);
+ avahi_browser_cleanup(s);
+}
+
+static void mcast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
+ AvahiServer *s = userdata;
+ AvahiAddress dest, src;
+ AvahiDnsPacket *p = NULL;
+ AvahiIfIndex iface;
+ uint16_t port;
+ uint8_t ttl;
+
+ assert(w);
+ assert(fd >= 0);
+ assert(events & AVAHI_WATCH_IN);
+
+ if (fd == s->fd_ipv4) {
+ dest.proto = src.proto = AVAHI_PROTO_INET;
+ p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &src.data.ipv4, &port, &dest.data.ipv4, &iface, &ttl);
+ } else {
+ assert(fd == s->fd_ipv6);
+ dest.proto = src.proto = AVAHI_PROTO_INET6;
+ p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &src.data.ipv6, &port, &dest.data.ipv6, &iface, &ttl);
+ }
+
+ if (p) {
+ if (iface == AVAHI_IF_UNSPEC)
+ iface = avahi_find_interface_for_address(s->monitor, &dest);
+
+ if (iface != AVAHI_IF_UNSPEC)
+ dispatch_packet(s, p, &src, port, &dest, iface, ttl);
+ else
+ avahi_log_error("Incoming packet recieved on address that isn't local.");
+
+ avahi_dns_packet_free(p);
+
+ cleanup_dead(s);
+ }
+}
+
+static void legacy_unicast_socket_event(AvahiWatch *w, int fd, AvahiWatchEvent events, void *userdata) {
+ AvahiServer *s = userdata;
+ AvahiDnsPacket *p = NULL;
+
+ assert(w);
+ assert(fd >= 0);
+ assert(events & AVAHI_WATCH_IN);
+
+ if (fd == s->fd_legacy_unicast_ipv4)
+ p = avahi_recv_dns_packet_ipv4(s->fd_legacy_unicast_ipv4, NULL, NULL, NULL, NULL, NULL);
+ else {
+ assert(fd == s->fd_legacy_unicast_ipv6);
+ p = avahi_recv_dns_packet_ipv6(s->fd_legacy_unicast_ipv6, NULL, NULL, NULL, NULL, NULL);
+ }
+
+ if (p) {
+ dispatch_legacy_unicast_packet(s, p);
+ avahi_dns_packet_free(p);
+
+ cleanup_dead(s);
+ }
+}
+
+static void server_set_state(AvahiServer *s, AvahiServerState state) {
+ assert(s);
+
+ if (s->state == state)
+ return;
+
+ s->state = state;
+
+ avahi_interface_monitor_update_rrs(s->monitor, 0);
+
+ if (s->callback)
+ s->callback(s, state, s->userdata);
+}
+
+static void withdraw_host_rrs(AvahiServer *s) {
+ assert(s);
+
+ if (s->hinfo_entry_group)
+ avahi_s_entry_group_reset(s->hinfo_entry_group);
+
+ if (s->browse_domain_entry_group)
+ avahi_s_entry_group_reset(s->browse_domain_entry_group);
+
+ avahi_interface_monitor_update_rrs(s->monitor, 1);
+ s->n_host_rr_pending = 0;
+}
+
+void avahi_server_decrease_host_rr_pending(AvahiServer *s) {
+ assert(s);
+
+ assert(s->n_host_rr_pending > 0);
+
+ if (--s->n_host_rr_pending == 0)
+ server_set_state(s, AVAHI_SERVER_RUNNING);
+}
+
+void avahi_host_rr_entry_group_callback(AvahiServer *s, AvahiSEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) {
+ assert(s);
+ assert(g);
+
+ if (state == AVAHI_ENTRY_GROUP_REGISTERING &&
+ s->state == AVAHI_SERVER_REGISTERING)
+ s->n_host_rr_pending ++;
+
+ else if (state == AVAHI_ENTRY_GROUP_COLLISION &&
+ (s->state == AVAHI_SERVER_REGISTERING || s->state == AVAHI_SERVER_RUNNING)) {
+ withdraw_host_rrs(s);
+ server_set_state(s, AVAHI_SERVER_COLLISION);
+
+ } else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED &&
+ s->state == AVAHI_SERVER_REGISTERING)
+ avahi_server_decrease_host_rr_pending(s);
+}
+
+static void register_hinfo(AvahiServer *s) {
+ struct utsname utsname;
+ AvahiRecord *r;
+
+ assert(s);
+
+ if (!s->config.publish_hinfo)
+ return;
+
+ if (s->hinfo_entry_group)
+ assert(avahi_s_entry_group_is_empty(s->hinfo_entry_group));
+ else
+ s->hinfo_entry_group = avahi_s_entry_group_new(s, avahi_host_rr_entry_group_callback, NULL);
+
+ if (!s->hinfo_entry_group) {
+ avahi_log_warn("Failed to create HINFO entry group: %s", avahi_strerror(s->error));
+ return;
+ }
+
+ /* Fill in HINFO rr */
+ if ((r = avahi_record_new_full(s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL_HOST_NAME))) {
+
+ if (uname(&utsname) < 0)
+ avahi_log_warn("uname() failed: %s\n", avahi_strerror(errno));
+ else {
+
+ r->data.hinfo.cpu = avahi_strdup(avahi_strup(utsname.machine));
+ r->data.hinfo.os = avahi_strdup(avahi_strup(utsname.sysname));
+
+ avahi_log_info("Registering HINFO record with values '%s'/'%s'.", r->data.hinfo.cpu, r->data.hinfo.os);
+
+ if (avahi_server_add(s, s->hinfo_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_UNIQUE, r) < 0) {
+ avahi_log_warn("Failed to add HINFO RR: %s", avahi_strerror(s->error));
+ return;
+ }
+ }
+
+ avahi_record_unref(r);
+ }
+
+ if (avahi_s_entry_group_commit(s->hinfo_entry_group) < 0)
+ avahi_log_warn("Failed to commit HINFO entry group: %s", avahi_strerror(s->error));
+
+}
+
+static void register_localhost(AvahiServer *s) {
+ AvahiAddress a;
+ assert(s);
+
+ /* Add localhost entries */
+ avahi_address_parse("127.0.0.1", AVAHI_PROTO_INET, &a);
+ avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "localhost", &a);
+
+ avahi_address_parse("::1", AVAHI_PROTO_INET6, &a);
+ avahi_server_add_address(s, NULL, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_PUBLISH_NO_PROBE|AVAHI_PUBLISH_NO_ANNOUNCE, "ip6-localhost", &a);
+}
+
+static void register_browse_domain(AvahiServer *s) {
+ assert(s);
+
+ if (!s->config.publish_domain)
+ return;
+
+ if (avahi_domain_equal(s->domain_name, "local"))
+ return;
+
+ if (s->browse_domain_entry_group)
+ assert(avahi_s_entry_group_is_empty(s->browse_domain_entry_group));
+ else
+ s->browse_domain_entry_group = avahi_s_entry_group_new(s, NULL, NULL);
+
+ if (!s->browse_domain_entry_group) {
+ avahi_log_warn("Failed to create browse domain entry group: %s", avahi_strerror(s->error));
+ return;
+ }
+
+ if (avahi_server_add_ptr(s, s->browse_domain_entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, AVAHI_DEFAULT_TTL, "b._dns-sd._udp.local", s->domain_name) < 0) {
+ avahi_log_warn("Failed to add browse domain RR: %s", avahi_strerror(s->error));
+ return;
+ }
+
+ if (avahi_s_entry_group_commit(s->browse_domain_entry_group) < 0)
+ avahi_log_warn("Failed to commit browse domain entry group: %s", avahi_strerror(s->error));
+}
+
+static void register_stuff(AvahiServer *s) {
+ assert(s);
+
+ server_set_state(s, AVAHI_SERVER_REGISTERING);
+ s->n_host_rr_pending ++; /** Make sure that the state isn't changed tp AVAHI_SERVER_RUNNING too early */
+
+ register_hinfo(s);
+ register_browse_domain(s);
+ avahi_interface_monitor_update_rrs(s->monitor, 0);
+
+ s->n_host_rr_pending --;
+
+ if (s->n_host_rr_pending == 0)
+ server_set_state(s, AVAHI_SERVER_RUNNING);
+}
+
+static void update_fqdn(AvahiServer *s) {
+ char *n;
+
+ assert(s);
+ assert(s->host_name);
+ assert(s->domain_name);
+
+ if (!(n = avahi_strdup_printf("%s.%s", s->host_name, s->domain_name)))
+ return; /* OOM */
+
+ avahi_free(s->host_name_fqdn);
+ s->host_name_fqdn = n;
+}
+
+int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
+ char *hn = NULL;
+ assert(s);
+
+ AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
+
+ if (!host_name) {
+ hn = avahi_get_host_name_strdup();
+ hn[strcspn(hn, ".")] = 0;
+ host_name = hn;
+ }
+
+ if (avahi_domain_equal(s->host_name, host_name) && s->state != AVAHI_SERVER_COLLISION) {
+ avahi_free(hn);
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
+ }
+
+ withdraw_host_rrs(s);
+
+ avahi_free(s->host_name);
+ s->host_name = hn ? hn : avahi_strdup(host_name);
+
+ update_fqdn(s);
+
+ register_stuff(s);
+ return AVAHI_OK;
+}
+
+int avahi_server_set_domain_name(AvahiServer *s, const char *domain_name) {
+ char *dn = NULL;
+ assert(s);
+
+ AVAHI_CHECK_VALIDITY(s, !domain_name || avahi_is_valid_domain_name(domain_name), AVAHI_ERR_INVALID_DOMAIN_NAME);
+
+ if (!domain_name) {
+ dn = avahi_strdup("local");
+ domain_name = dn;
+ }
+
+ if (avahi_domain_equal(s->domain_name, domain_name)) {
+ avahi_free(dn);
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
+ }
+
+ withdraw_host_rrs(s);
+
+ avahi_free(s->domain_name);
+ s->domain_name = avahi_normalize_name_strdup(domain_name);
+ update_fqdn(s);
+
+ register_stuff(s);
+
+ avahi_free(dn);
+ return AVAHI_OK;
+}
+
+static int valid_server_config(const AvahiServerConfig *sc) {
+
+ if (sc->host_name && !avahi_is_valid_host_name(sc->host_name))
+ return AVAHI_ERR_INVALID_HOST_NAME;
+
+ if (sc->domain_name && !avahi_is_valid_domain_name(sc->domain_name))
+ return AVAHI_ERR_INVALID_DOMAIN_NAME;
+
+ return AVAHI_OK;
+}
+
+static int setup_sockets(AvahiServer *s) {
+ assert(s);
+
+ s->fd_ipv4 = s->config.use_ipv4 ? avahi_open_socket_ipv4(s->config.disallow_other_stacks) : -1;
+ s->fd_ipv6 = s->config.use_ipv6 ? avahi_open_socket_ipv6(s->config.disallow_other_stacks) : -1;
+
+ if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0)
+ return AVAHI_ERR_NO_NETWORK;
+
+ if (s->fd_ipv4 < 0 && s->config.use_ipv4)
+ avahi_log_notice("Failed to create IPv4 socket, proceeding in IPv6 only mode");
+ 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_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, mcast_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, mcast_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, legacy_unicast_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, legacy_unicast_socket_event, s);
+
+ return 0;
+}
+
+AvahiServer *avahi_server_new(const AvahiPoll *poll_api, const AvahiServerConfig *sc, AvahiServerCallback callback, void* userdata, int *error) {
+ AvahiServer *s;
+ int e;
+
+ if (sc && (e = valid_server_config(sc)) < 0) {
+ if (error)
+ *error = e;
+ return NULL;
+ }
+
+ if (!(s = avahi_new(AvahiServer, 1))) {
+ if (error)
+ *error = AVAHI_ERR_NO_MEMORY;
+
+ return NULL;
+ }
+
+ s->poll_api = poll_api;
+
+ if (sc)
+ avahi_server_config_copy(&s->config, sc);
+ else
+ avahi_server_config_init(&s->config);
+
+ if ((e = setup_sockets(s)) < 0) {
+ if (error)
+ *error = e;
+
+ avahi_server_config_free(&s->config);
+ avahi_free(s);
+
+ return NULL;
+ }
+
+ s->n_host_rr_pending = 0;
+ s->need_entry_cleanup = 0;
+ s->need_group_cleanup = 0;
+ s->need_browser_cleanup = 0;
+ s->hinfo_entry_group = NULL;
+ s->browse_domain_entry_group = NULL;
+ s->error = AVAHI_OK;
+ s->state = AVAHI_SERVER_INVALID;
+
+ s->callback = callback;
+ s->userdata = userdata;
+
+ s->time_event_queue = avahi_time_event_queue_new(poll_api);
+
+ s->entries_by_key = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
+ AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
+ AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
+
+ s->record_browser_hashmap = avahi_hashmap_new((AvahiHashFunc) avahi_key_hash, (AvahiEqualFunc) avahi_key_equal, NULL, NULL);
+ AVAHI_LLIST_HEAD_INIT(AvahiSRecordBrowser, s->record_browsers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSHostNameResolver, s->host_name_resolvers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSAddressResolver, s->address_resolvers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSDomainBrowser, s->domain_browsers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSServiceTypeBrowser, s->service_type_browsers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSServiceBrowser, s->service_browsers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSServiceResolver, s->service_resolvers);
+ AVAHI_LLIST_HEAD_INIT(AvahiSDNSServerBrowser, s->dns_server_browsers);
+
+ s->legacy_unicast_reflect_slots = NULL;
+ s->legacy_unicast_reflect_id = 0;
+
+ s->record_list = avahi_record_list_new();
+
+ /* Get host name */
+ s->host_name = s->config.host_name ? avahi_normalize_name_strdup(s->config.host_name) : avahi_get_host_name_strdup();
+ s->host_name[strcspn(s->host_name, ".")] = 0;
+ s->domain_name = s->config.domain_name ? avahi_normalize_name_strdup(s->config.domain_name) : avahi_strdup("local");
+ s->host_name_fqdn = NULL;
+ update_fqdn(s);
+
+ do {
+ s->local_service_cookie = (uint32_t) rand() * (uint32_t) rand();
+ } while (s->local_service_cookie == AVAHI_SERVICE_COOKIE_INVALID);
+
+ 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);
+
+ s->monitor = avahi_interface_monitor_new(s);
+ avahi_interface_monitor_sync(s->monitor);
+
+ register_localhost(s);
+ register_stuff(s);
+
+ return s;
+}
+
+void avahi_server_free(AvahiServer* s) {
+ assert(s);
+
+ /* Remove all browsers */
+
+ while (s->dns_server_browsers)
+ avahi_s_dns_server_browser_free(s->dns_server_browsers);
+ while (s->host_name_resolvers)
+ avahi_s_host_name_resolver_free(s->host_name_resolvers);
+ while (s->address_resolvers)
+ avahi_s_address_resolver_free(s->address_resolvers);
+ while (s->domain_browsers)
+ avahi_s_domain_browser_free(s->domain_browsers);
+ while (s->service_type_browsers)
+ avahi_s_service_type_browser_free(s->service_type_browsers);
+ while (s->service_browsers)
+ avahi_s_service_browser_free(s->service_browsers);
+ while (s->service_resolvers)
+ avahi_s_service_resolver_free(s->service_resolvers);
+ while (s->record_browsers)
+ avahi_s_record_browser_destroy(s->record_browsers);
+
+ /* Remove all locally rgeistered stuff */
+
+ while(s->entries)
+ avahi_entry_free(s, s->entries);
+
+ avahi_interface_monitor_free(s->monitor);
+
+ while (s->groups)
+ avahi_entry_group_free(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);
+
+ avahi_server_config_free(&s->config);
+
+ avahi_free(s);
+}
+
+const char* avahi_server_get_domain_name(AvahiServer *s) {
+ assert(s);
+
+ return s->domain_name;
+}
+
+const char* avahi_server_get_host_name(AvahiServer *s) {
+ assert(s);
+
+ return s->host_name;
+}
+
+const char* avahi_server_get_host_name_fqdn(AvahiServer *s) {
+ assert(s);
+
+ return s->host_name_fqdn;
+}
+
+void* avahi_server_get_data(AvahiServer *s) {
+ assert(s);
+
+ return s->userdata;
+}
+
+void avahi_server_set_data(AvahiServer *s, void* userdata) {
+ assert(s);
+
+ s->userdata = userdata;
+}
+
+AvahiServerState avahi_server_get_state(AvahiServer *s) {
+ assert(s);
+
+ return s->state;
+}
+
+AvahiServerConfig* avahi_server_config_init(AvahiServerConfig *c) {
+ assert(c);
+
+ memset(c, 0, sizeof(AvahiServerConfig));
+ c->use_ipv6 = 1;
+ c->use_ipv4 = 1;
+ c->host_name = NULL;
+ c->domain_name = NULL;
+ c->check_response_ttl = 0;
+ c->publish_hinfo = 1;
+ c->publish_addresses = 1;
+ c->publish_workstation = 1;
+ c->publish_domain = 1;
+ c->use_iff_running = 0;
+ c->enable_reflector = 0;
+ c->reflect_ipv = 0;
+ c->add_service_cookie = 1;
+ c->enable_wide_area = 0;
+ c->n_wide_area_servers = 0;
+ c->disallow_other_stacks = 0;
+ c->browse_domains = NULL;
+ c->disable_publishing = 0;
+ c->allow_point_to_point = 0;
+
+ return c;
+}
+
+void avahi_server_config_free(AvahiServerConfig *c) {
+ assert(c);
+
+ avahi_free(c->host_name);
+ avahi_free(c->domain_name);
+ avahi_string_list_free(c->browse_domains);
+}
+
+AvahiServerConfig* avahi_server_config_copy(AvahiServerConfig *ret, const AvahiServerConfig *c) {
+ char *d = NULL, *h = NULL;
+ AvahiStringList *l = NULL;
+ assert(ret);
+ assert(c);
+
+ if (c->host_name)
+ if (!(h = avahi_strdup(c->host_name)))
+ return NULL;
+
+ if (c->domain_name)
+ if (!(d = avahi_strdup(c->domain_name))) {
+ avahi_free(h);
+ return NULL;
+ }
+
+ if (!(l = avahi_string_list_copy(c->browse_domains)) && c->browse_domains) {
+ avahi_free(h);
+ avahi_free(d);
+ return NULL;
+ }
+
+ *ret = *c;
+ ret->host_name = h;
+ ret->domain_name = d;
+ ret->browse_domains = l;
+
+ return ret;
+}
+
+int avahi_server_errno(AvahiServer *s) {
+ assert(s);
+
+ return s->error;
+}
+
+/* Just for internal use */
+int avahi_server_set_errno(AvahiServer *s, int error) {
+ assert(s);
+
+ return s->error = error;
+}
+
+uint32_t avahi_server_get_local_service_cookie(AvahiServer *s) {
+ assert(s);
+
+ return s->local_service_cookie;
+}
+
+static AvahiEntry *find_entry(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiKey *key) {
+ AvahiEntry *e;
+
+ assert(s);
+ assert(key);
+
+ for (e = avahi_hashmap_lookup(s->entries_by_key, key); e; e = e->by_key_next)
+
+ if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
+ (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
+ (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING))
+
+ return e;
+
+ return NULL;
+}
+
+int avahi_server_get_group_of_service(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain, AvahiSEntryGroup** ret_group) {
+ AvahiKey *key = NULL;
+ AvahiEntry *e;
+ int ret;
+ char n[AVAHI_DOMAIN_NAME_MAX];
+
+ assert(s);
+ assert(name);
+ assert(type);
+ assert(ret_group);
+
+ AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
+ AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
+ AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
+ AVAHI_CHECK_VALIDITY(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
+ AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
+
+ if ((ret = avahi_service_name_join(n, sizeof(n), name, type, domain) < 0))
+ return avahi_server_set_errno(s, ret);
+
+ if (!(key = avahi_key_new(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
+
+ e = find_entry(s, interface, protocol, key);
+ avahi_key_unref(key);
+
+ if (e) {
+ *ret_group = e->group;
+ return AVAHI_OK;
+ }
+
+ return avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
+}
+
+int avahi_server_is_service_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, const char *name) {
+ AvahiKey *key = NULL;
+ AvahiEntry *e;
+
+ assert(s);
+ assert(name);
+
+ if (!s->host_name_fqdn)
+ return 0;
+
+ if (!(key = avahi_key_new(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV)))
+ return 0;
+
+ e = find_entry(s, interface, protocol, key);
+ avahi_key_unref(key);
+
+ if (!e)
+ return 0;
+
+ return avahi_domain_equal(s->host_name_fqdn, e->record->data.srv.name);
+}
+
+int avahi_server_is_record_local(AvahiServer *s, AvahiIfIndex interface, AvahiProtocol protocol, AvahiRecord *record) {
+ AvahiEntry *e;
+
+ assert(s);
+ assert(record);
+
+ for (e = avahi_hashmap_lookup(s->entries_by_key, record->key); e; e = e->by_key_next)
+
+ if ((e->interface == interface || e->interface <= 0 || interface <= 0) &&
+ (e->protocol == protocol || e->protocol == AVAHI_PROTO_UNSPEC || protocol == AVAHI_PROTO_UNSPEC) &&
+ (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED || e->group->state == AVAHI_ENTRY_GROUP_REGISTERING) &&
+ avahi_record_equal_no_ttl(record, e->record))
+ return 1;
+
+ return 0;
+}
+
+/** 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/trunk/avahi-core/socket.c b/trunk/avahi-core/socket.c
new file mode 100644
index 0000000..9291f08
--- /dev/null
+++ b/trunk/avahi-core/socket.c
@@ -0,0 +1,930 @@
+/* $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 <inttypes.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <sys/uio.h>
+
+#ifdef IP_RECVIF
+#include <net/if_dl.h>
+#endif
+
+#include "dns.h"
+#include "fdutil.h"
+#include "socket.h"
+#include "log.h"
+#include "addr-util.h"
+
+/* this is a portability hack */
+#ifndef IPV6_ADD_MEMBERSHIP
+#ifdef IPV6_JOIN_GROUP
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#endif
+#endif
+
+#ifndef IPV6_DROP_MEMBERSHIP
+#ifdef IPV6_LEAVE_GROUP
+#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
+#endif
+#endif
+
+static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
+ assert(ret_sa);
+
+ memset(ret_sa, 0, sizeof(struct sockaddr_in));
+ ret_sa->sin_family = AF_INET;
+ ret_sa->sin_port = htons(AVAHI_MDNS_PORT);
+ inet_pton(AF_INET, AVAHI_IPV4_MCAST_GROUP, &ret_sa->sin_addr);
+}
+
+static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
+ assert(ret_sa);
+
+ memset(ret_sa, 0, sizeof(struct sockaddr_in6));
+ ret_sa->sin6_family = AF_INET6;
+ ret_sa->sin6_port = htons(AVAHI_MDNS_PORT);
+ inet_pton(AF_INET6, AVAHI_IPV6_MCAST_GROUP, &ret_sa->sin6_addr);
+}
+
+static void ipv4_address_to_sockaddr(struct sockaddr_in *ret_sa, const AvahiIPv4Address *a, uint16_t port) {
+ assert(ret_sa);
+ assert(a);
+ assert(port > 0);
+
+ memset(ret_sa, 0, sizeof(struct sockaddr_in));
+ ret_sa->sin_family = AF_INET;
+ ret_sa->sin_port = htons(port);
+ memcpy(&ret_sa->sin_addr, a, sizeof(AvahiIPv4Address));
+}
+
+static void ipv6_address_to_sockaddr(struct sockaddr_in6 *ret_sa, const AvahiIPv6Address *a, uint16_t port) {
+ assert(ret_sa);
+ assert(a);
+ assert(port > 0);
+
+ memset(ret_sa, 0, sizeof(struct sockaddr_in6));
+ ret_sa->sin6_family = AF_INET6;
+ ret_sa->sin6_port = htons(port);
+ memcpy(&ret_sa->sin6_addr, a, sizeof(AvahiIPv6Address));
+}
+
+int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *a, int idx, int join) {
+#ifdef HAVE_STRUCT_IP_MREQN
+ struct ip_mreqn mreq;
+#else
+ struct ip_mreq mreq;
+#endif
+ struct sockaddr_in sa;
+
+ assert(fd >= 0);
+ assert(idx >= 0);
+ assert(a);
+
+ memset(&mreq, 0, sizeof(mreq));
+#ifdef HAVE_STRUCT_IP_MREQN
+ mreq.imr_ifindex = idx;
+ mreq.imr_address.s_addr = a->address;
+#else
+ mreq.imr_interface.s_addr = a->address;
+#endif
+ mdns_mcast_group_ipv4(&sa);
+ mreq.imr_multiaddr = sa.sin_addr;
+
+ /* Some network drivers have issues with dropping membership of
+ * mcast groups when the iface is down, but don't allow rejoining
+ * when it comes back up. This is an ugly workaround */
+ if (join)
+ setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
+
+ if (setsockopt(fd, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+ avahi_log_warn("%s failed: %s", join ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *a, int idx, int join) {
+ struct ipv6_mreq mreq6;
+ struct sockaddr_in6 sa6;
+
+ assert(fd >= 0);
+ assert(idx >= 0);
+ assert(a);
+
+ memset(&mreq6, 0, sizeof(mreq6));
+ mdns_mcast_group_ipv6 (&sa6);
+ mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
+ mreq6.ipv6mr_interface = idx;
+
+ if (join)
+ setsockopt(fd, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6));
+
+ if (setsockopt(fd, IPPROTO_IPV6, join ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
+ avahi_log_warn("%s failed: %s", join ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int reuseaddr(int fd) {
+ int yes;
+
+ yes = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("SO_REUSEADDR failed: %s", strerror(errno));
+ return -1;
+ }
+
+#ifdef SO_REUSEPORT
+ yes = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("SO_REUSEPORT failed: %s", strerror(errno));
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+static int bind_with_warn(int fd, const struct sockaddr *sa, socklen_t l) {
+
+ assert(fd >= 0);
+ assert(sa);
+ assert(l > 0);
+
+ if (bind(fd, sa, l) < 0) {
+
+ if (errno != EADDRINUSE) {
+ avahi_log_warn("bind() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ avahi_log_warn("*** WARNING: Detected another %s mDNS stack running on this host. This makes mDNS unreliable and is thus not recommended. ***",
+ sa->sa_family == AF_INET ? "IPv4" : "IPv6");
+
+ /* Try again, this time with SO_REUSEADDR set */
+ if (reuseaddr(fd) < 0)
+ return -1;
+
+ if (bind(fd, sa, l) < 0) {
+ avahi_log_warn("bind() failed: %s", strerror(errno));
+ return -1;
+ }
+ } else {
+
+ /* We enable SO_REUSEADDR afterwards, to make sure that the
+ * user may run other mDNS implementations if he really
+ * wants. */
+
+ if (reuseaddr(fd) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int ipv4_pktinfo(int fd) {
+ int yes;
+
+#ifdef IP_PKTINFO
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IP_PKTINFO failed: %s", strerror(errno));
+ return -1;
+ }
+#else
+
+#ifdef IP_RECVINTERFACE
+ yes = 1;
+ if (setsockopt (fd, IPPROTO_IP, IP_RECVINTERFACE, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IP_RECVINTERFACE failed: %s", strerror(errno));
+ return -1;
+ }
+#elif defined(IP_RECVIF)
+ yes = 1;
+ if (setsockopt (fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IP_RECVIF failed: %s", strerror(errno));
+ return -1;
+ }
+#endif
+
+#ifdef IP_RECVDSTADDR
+ yes = 1;
+ if (setsockopt (fd, IPPROTO_IP, IP_RECVDSTADDR, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IP_RECVDSTADDR failed: %s", strerror(errno));
+ return -1;
+ }
+#endif
+
+#endif /* IP_PKTINFO */
+
+#ifdef IP_RECVTTL
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IP_RECVTTL failed: %s", strerror(errno));
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+static int ipv6_pktinfo(int fd) {
+ int yes;
+
+#ifdef IPV6_RECVPKTINFO
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IPV6_RECVPKTINFO failed: %s", strerror(errno));
+ return -1;
+ }
+#elif defined(IPV6_PKTINFO)
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IPV6_PKTINFO failed: %s", strerror(errno));
+ return -1;
+ }
+#endif
+
+#ifdef IPV6_RECVHOPS
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPS, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IPV6_RECVHOPS failed: %s", strerror(errno));
+ return -1;
+ }
+#elif defined(IPV6_RECVHOPLIMIT)
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IPV6_RECVHOPLIMIT failed: %s", strerror(errno));
+ return -1;
+ }
+#elif defined(IPV6_HOPLIMIT)
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IPV6_HOPLIMIT failed: %s", strerror(errno));
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+int avahi_open_socket_ipv4(int no_reuse) {
+ struct sockaddr_in local;
+ int fd = -1, r, ittl;
+ uint8_t ttl, cyes;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ ttl = 255;
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
+ avahi_log_warn("IP_MULTICAST_TTL failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ ittl = 255;
+ if (setsockopt(fd, IPPROTO_IP, IP_TTL, &ittl, sizeof(ittl)) < 0) {
+ avahi_log_warn("IP_TTL failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ cyes = 1;
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &cyes, sizeof(cyes)) < 0) {
+ avahi_log_warn("IP_MULTICAST_LOOP failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ memset(&local, 0, sizeof(local));
+ local.sin_family = AF_INET;
+ local.sin_port = htons(AVAHI_MDNS_PORT);
+
+ if (no_reuse)
+ r = bind(fd, (struct sockaddr*) &local, sizeof(local));
+ else
+ r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
+
+ if (r < 0)
+ goto fail;
+
+ if (ipv4_pktinfo (fd) < 0)
+ goto fail;
+
+ if (avahi_set_cloexec(fd) < 0) {
+ avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (avahi_set_nonblock(fd) < 0) {
+ avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+int avahi_open_socket_ipv6(int no_reuse) {
+ struct sockaddr_in6 sa, local;
+ int fd = -1, yes, r;
+ int ttl;
+
+ mdns_mcast_group_ipv6(&sa);
+
+ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ ttl = 255;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
+ avahi_log_warn("IPV6_MULTICAST_HOPS failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ ttl = 255;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
+ avahi_log_warn("IPV6_UNICAST_HOPS failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IPV6_V6ONLY failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
+ avahi_log_warn("IPV6_MULTICAST_LOOP failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ memset(&local, 0, sizeof(local));
+ local.sin6_family = AF_INET6;
+ local.sin6_port = htons(AVAHI_MDNS_PORT);
+
+ if (no_reuse)
+ r = bind(fd, (struct sockaddr*) &local, sizeof(local));
+ else
+ r = bind_with_warn(fd, (struct sockaddr*) &local, sizeof(local));
+
+ if (r < 0)
+ goto fail;
+
+ if (ipv6_pktinfo(fd) < 0)
+ goto fail;
+
+ if (avahi_set_cloexec(fd) < 0) {
+ avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (avahi_set_nonblock(fd) < 0) {
+ avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+static int sendmsg_loop(int fd, struct msghdr *msg, int flags) {
+ assert(fd >= 0);
+ assert(msg);
+
+ for (;;) {
+
+ if (sendmsg(fd, msg, flags) >= 0)
+ break;
+
+ if (errno != EAGAIN) {
+ avahi_log_debug("sendmsg() failed: %s", strerror(errno));
+ return -1;
+ }
+
+ if (avahi_wait_for_write(fd) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int avahi_send_dns_packet_ipv4(
+ int fd,
+ AvahiIfIndex interface,
+ AvahiDnsPacket *p,
+ const AvahiIPv4Address *src_address,
+ const AvahiIPv4Address *dst_address,
+ uint16_t dst_port) {
+
+ struct sockaddr_in sa;
+ struct msghdr msg;
+ struct iovec io;
+#ifdef IP_PKTINFO
+ struct cmsghdr *cmsg;
+ uint8_t cmsg_data[CMSG_SPACE(sizeof(struct in_pktinfo))];
+#elif defined(IP_SENDSRCADDR)
+ struct cmsghdr *cmsg;
+ uint8_t cmsg_data[CMSG_SPACE(sizeof(struct in_addr))];
+#endif
+
+ assert(fd >= 0);
+ assert(p);
+ assert(avahi_dns_packet_check_valid(p) >= 0);
+ assert(!dst_address || dst_port > 0);
+
+ if (!dst_address)
+ mdns_mcast_group_ipv4(&sa);
+ else
+ ipv4_address_to_sockaddr(&sa, dst_address, dst_port);
+
+ memset(&io, 0, sizeof(io));
+ io.iov_base = AVAHI_DNS_PACKET_DATA(p);
+ io.iov_len = p->size;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_iov = &io;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+
+#ifdef IP_PKTINFO
+ if (interface > 0 || src_address) {
+ struct in_pktinfo *pkti;
+
+ memset(cmsg_data, 0, sizeof(cmsg_data));
+ msg.msg_control = cmsg_data;
+ msg.msg_controllen = sizeof(cmsg_data);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+
+ pkti = (struct in_pktinfo*) CMSG_DATA(cmsg);
+
+ if (interface > 0)
+ pkti->ipi_ifindex = interface;
+
+ if (src_address)
+ pkti->ipi_spec_dst.s_addr = src_address->address;
+
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+#elif defined(IP_SENDSRCADDR)
+ if (src_address) {
+ struct in_addr *addr;
+
+ memset(cmsg_data, 0, sizeof(cmsg_data));
+ msg.msg_control = cmsg_data;
+ msg.msg_controllen = sizeof(cmsg_data);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_SENDSRCADDR;
+
+ addr = (struct in_addr *)CMSG_DATA(cmsg);
+ addr->s_addr = src_address->address;
+
+ msg.msg_controllen = cmsg->cmsg_len;
+}
+#elif defined(IP_MULTICAST_IF)
+ {
+ struct in_addr any = { INADDR_ANY };
+ if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, src_address ? (const void*) &src_address->address : (const void*) &any, sizeof(struct in_addr)) < 0) {
+ avahi_log_warn("IP_MULTICAST_IF failed: %s", strerror(errno));
+ return -1;
+ }
+ }
+#elif defined(__GNUC__)
+#warning "FIXME: We need some code to set the outgoing interface/local address here if IP_PKTINFO/IP_MULTICAST_IF is not available"
+#endif
+
+ return sendmsg_loop(fd, &msg, 0);
+}
+
+int avahi_send_dns_packet_ipv6(int fd, AvahiIfIndex interface, AvahiDnsPacket *p, const AvahiIPv6Address *src_address, const AvahiIPv6Address *dst_address, uint16_t dst_port) {
+ struct sockaddr_in6 sa;
+ struct msghdr msg;
+ struct iovec io;
+ struct cmsghdr *cmsg;
+ uint8_t cmsg_data[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+
+ assert(fd >= 0);
+ assert(p);
+ assert(avahi_dns_packet_check_valid(p) >= 0);
+ assert(!dst_address || dst_port > 0);
+
+ if (!dst_address)
+ mdns_mcast_group_ipv6(&sa);
+ else
+ ipv6_address_to_sockaddr(&sa, dst_address, dst_port);
+
+ memset(&io, 0, sizeof(io));
+ io.iov_base = AVAHI_DNS_PACKET_DATA(p);
+ io.iov_len = p->size;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_iov = &io;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+
+ if (interface > 0 || src_address) {
+ struct in6_pktinfo *pkti;
+
+ memset(cmsg_data, 0, sizeof(cmsg_data));
+ msg.msg_control = cmsg_data;
+ msg.msg_controllen = sizeof(cmsg_data);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+
+ pkti = (struct in6_pktinfo*) CMSG_DATA(cmsg);
+
+ if (interface > 0)
+ pkti->ipi6_ifindex = interface;
+
+ if (src_address)
+ memcpy(&pkti->ipi6_addr, src_address->address, sizeof(src_address->address));
+
+ msg.msg_controllen = cmsg->cmsg_len;
+ } else {
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ }
+
+ return sendmsg_loop(fd, &msg, 0);
+}
+
+AvahiDnsPacket *avahi_recv_dns_packet_ipv4(int fd, AvahiIPv4Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv4Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl) {
+ AvahiDnsPacket *p= NULL;
+ struct msghdr msg;
+ struct iovec io;
+ size_t aux[1024 / sizeof(size_t)]; /* for alignment on ia64 ! */
+ ssize_t l;
+ struct cmsghdr *cmsg;
+ int found_addr = 0;
+ int ms;
+ struct sockaddr_in sa;
+
+ assert(fd >= 0);
+
+ if (ioctl(fd, FIONREAD, &ms) < 0) {
+ avahi_log_warn("ioctl(): %s", strerror(errno));
+ goto fail;
+ }
+
+ p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
+
+ io.iov_base = AVAHI_DNS_PACKET_DATA(p);
+ io.iov_len = p->max_size;
+
+ 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 = aux;
+ msg.msg_controllen = sizeof(aux);
+ msg.msg_flags = 0;
+
+ if ((l = recvmsg(fd, &msg, 0)) < 0) {
+ avahi_log_warn("recvmsg(): %s", strerror(errno));
+ goto fail;
+ }
+
+ if (sa.sin_addr.s_addr == INADDR_ANY) {
+ /* Linux 2.4 behaves very strangely sometimes! */
+ goto fail;
+ }
+
+ assert(!(msg.msg_flags & MSG_CTRUNC));
+ assert(!(msg.msg_flags & MSG_TRUNC));
+
+ p->size = (size_t) l;
+
+ if (ret_src_port)
+ *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
+
+ if (ret_src_address) {
+ AvahiAddress a;
+ avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
+ *ret_src_address = a.data.ipv4;
+ }
+
+ if (ret_ttl)
+ *ret_ttl = 255;
+
+ if (ret_iface)
+ *ret_iface = AVAHI_IF_UNSPEC;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+
+ if (cmsg->cmsg_level == IPPROTO_IP) {
+
+ switch (cmsg->cmsg_type) {
+#ifdef IP_RECVTTL
+ case IP_RECVTTL:
+#endif
+ case IP_TTL:
+ if (ret_ttl)
+ *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
+
+ break;
+
+#ifdef IP_PKTINFO
+ case IP_PKTINFO: {
+ struct in_pktinfo *i = (struct in_pktinfo*) CMSG_DATA(cmsg);
+
+ if (ret_iface)
+ *ret_iface = (int) i->ipi_ifindex;
+
+ if (ret_dst_address)
+ ret_dst_address->address = i->ipi_addr.s_addr;
+
+ found_addr = 1;
+
+ break;
+ }
+#endif
+
+#ifdef IP_RECVIF
+ case IP_RECVIF: {
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *) CMSG_DATA (cmsg);
+
+ if (ret_iface)
+#ifdef __sun
+ *ret_iface = *(uint_t*) sdl;
+#else
+ *ret_iface = (int) sdl->sdl_index;
+#endif
+
+ break;
+ }
+#endif
+
+#ifdef IP_RECVDSTADDR
+ case IP_RECVDSTADDR:
+ if (ret_dst_address)
+ memcpy(&ret_dst_address->address, CMSG_DATA (cmsg), 4);
+
+ found_addr = 1;
+ break;
+#endif
+
+ default:
+ avahi_log_warn("Unhandled cmsg_type : %d", cmsg->cmsg_type);
+ break;
+ }
+ }
+ }
+
+ assert(found_addr);
+
+ return p;
+
+fail:
+ if (p)
+ avahi_dns_packet_free(p);
+
+ return NULL;
+}
+
+AvahiDnsPacket *avahi_recv_dns_packet_ipv6(int fd, AvahiIPv6Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv6Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl) {
+ AvahiDnsPacket *p = NULL;
+ struct msghdr msg;
+ struct iovec io;
+ size_t aux[1024 / sizeof(size_t)];
+ ssize_t l;
+ int ms;
+ struct cmsghdr *cmsg;
+ int found_ttl = 0, found_iface = 0;
+ struct sockaddr_in6 sa;
+
+ assert(fd >= 0);
+
+ if (ioctl(fd, FIONREAD, &ms) < 0) {
+ avahi_log_warn("ioctl(): %s", strerror(errno));
+ goto fail;
+ }
+
+ p = avahi_dns_packet_new(ms + AVAHI_DNS_PACKET_EXTRA_SIZE);
+
+ io.iov_base = AVAHI_DNS_PACKET_DATA(p);
+ io.iov_len = p->max_size;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = (struct sockaddr*) &sa;
+ msg.msg_namelen = sizeof(sa);
+
+ msg.msg_iov = &io;
+ msg.msg_iovlen = 1;
+ msg.msg_control = aux;
+ msg.msg_controllen = sizeof(aux);
+ msg.msg_flags = 0;
+
+ if ((l = recvmsg(fd, &msg, 0)) < 0) {
+ avahi_log_warn("recvmsg(): %s", strerror(errno));
+ goto fail;
+ }
+
+ assert(!(msg.msg_flags & MSG_CTRUNC));
+ assert(!(msg.msg_flags & MSG_TRUNC));
+
+ p->size = (size_t) l;
+
+ if (ret_src_port)
+ *ret_src_port = avahi_port_from_sockaddr((struct sockaddr*) &sa);
+
+ if (ret_src_address) {
+ AvahiAddress a;
+ avahi_address_from_sockaddr((struct sockaddr*) &sa, &a);
+ *ret_src_address = a.data.ipv6;
+ }
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+
+ if (cmsg->cmsg_level == IPPROTO_IPV6) {
+
+ switch (cmsg->cmsg_type) {
+
+ case IPV6_HOPLIMIT:
+
+ if (ret_ttl)
+ *ret_ttl = (uint8_t) (*(int *) CMSG_DATA(cmsg));
+
+ found_ttl = 1;
+
+ break;
+
+ case IPV6_PKTINFO: {
+ struct in6_pktinfo *i = (struct in6_pktinfo*) CMSG_DATA(cmsg);
+
+ if (ret_iface)
+ *ret_iface = i->ipi6_ifindex;
+
+ if (ret_dst_address)
+ memcpy(ret_dst_address->address, i->ipi6_addr.s6_addr, 16);
+
+ found_iface = 1;
+ break;
+ }
+
+ default:
+ avahi_log_warn("Unhandled cmsg_type : %d", cmsg->cmsg_type);
+ break;
+ }
+ }
+ }
+
+ assert(found_iface);
+ assert(found_ttl);
+
+ return p;
+
+fail:
+ if (p)
+ avahi_dns_packet_free(p);
+
+ return NULL;
+}
+
+int avahi_open_unicast_socket_ipv4(void) {
+ struct sockaddr_in local;
+ int fd = -1;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ memset(&local, 0, sizeof(local));
+ local.sin_family = AF_INET;
+
+ if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
+ avahi_log_warn("bind() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (ipv4_pktinfo(fd) < 0) {
+ goto fail;
+ }
+
+ if (avahi_set_cloexec(fd) < 0) {
+ avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (avahi_set_nonblock(fd) < 0) {
+ avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+int avahi_open_unicast_socket_ipv6(void) {
+ struct sockaddr_in6 local;
+ int fd = -1;
+
+ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ avahi_log_warn("socket() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ memset(&local, 0, sizeof(local));
+ local.sin6_family = AF_INET6;
+
+ if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
+ avahi_log_warn("bind() failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (ipv6_pktinfo(fd) < 0)
+ goto fail;
+
+ if (avahi_set_cloexec(fd) < 0) {
+ avahi_log_warn("FD_CLOEXEC failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (avahi_set_nonblock(fd) < 0) {
+ avahi_log_warn("O_NONBLOCK failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
diff --git a/trunk/avahi-core/socket.h b/trunk/avahi-core/socket.h
new file mode 100644
index 0000000..237b534
--- /dev/null
+++ b/trunk/avahi-core/socket.h
@@ -0,0 +1,49 @@
+#ifndef foosockethfoo
+#define foosockethfoo
+
+/* $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 <inttypes.h>
+
+#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(int no_reuse);
+int avahi_open_socket_ipv6(int no_reuse);
+
+int avahi_open_unicast_socket_ipv4(void);
+int avahi_open_unicast_socket_ipv6(void);
+
+int avahi_send_dns_packet_ipv4(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv4Address *src_address, const AvahiIPv4Address *dst_address, uint16_t dst_port);
+int avahi_send_dns_packet_ipv6(int fd, AvahiIfIndex iface, AvahiDnsPacket *p, const AvahiIPv6Address *src_address, const AvahiIPv6Address *dst_address, uint16_t dst_port);
+
+AvahiDnsPacket *avahi_recv_dns_packet_ipv4(int fd, AvahiIPv4Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv4Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl);
+AvahiDnsPacket *avahi_recv_dns_packet_ipv6(int fd, AvahiIPv6Address *ret_src_address, uint16_t *ret_src_port, AvahiIPv6Address *ret_dst_address, AvahiIfIndex *ret_iface, uint8_t *ret_ttl);
+
+int avahi_mdns_mcast_join_ipv4(int fd, const AvahiIPv4Address *local_address, int iface, int join);
+int avahi_mdns_mcast_join_ipv6(int fd, const AvahiIPv6Address *local_address, int iface, int join);
+
+#endif
diff --git a/trunk/avahi-core/timeeventq-test.c b/trunk/avahi-core/timeeventq-test.c
new file mode 100644
index 0000000..c66a55a
--- /dev/null
+++ b/trunk/avahi-core/timeeventq-test.c
@@ -0,0 +1,69 @@
+/* $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 <assert.h>
+#include <stdlib.h>
+
+#include <avahi-common/timeval.h>
+#include <avahi-common/simple-watch.h>
+
+#include "timeeventq.h"
+#include "log.h"
+
+#define POINTER_TO_INT(p) ((int) (p))
+#define INT_TO_POINTER(i) ((void*) (i))
+
+static AvahiTimeEventQueue *q = NULL;
+
+static void callback(AvahiTimeEvent*e, void* userdata) {
+ struct timeval tv = {0, 0};
+ assert(e);
+ avahi_log_info("callback(%i)", POINTER_TO_INT(userdata));
+ avahi_elapse_time(&tv, 1000, 100);
+ avahi_time_event_update(e, &tv);
+}
+
+int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) {
+ struct timeval tv;
+ AvahiSimplePoll *s;
+
+ s = avahi_simple_poll_new();
+
+ q = avahi_time_event_queue_new(avahi_simple_poll_get(s));
+
+ avahi_time_event_new(q, avahi_elapse_time(&tv, 5000, 100), callback, INT_TO_POINTER(1));
+ avahi_time_event_new(q, avahi_elapse_time(&tv, 5000, 100), callback, INT_TO_POINTER(2));
+
+ avahi_log_info("starting");
+
+ for (;;)
+ if (avahi_simple_poll_iterate(s, -1) != 0)
+ break;
+
+ avahi_time_event_queue_free(q);
+ avahi_simple_poll_free(s);
+
+ return 0;
+}
diff --git a/trunk/avahi-core/timeeventq.c b/trunk/avahi-core/timeeventq.c
new file mode 100644
index 0000000..17334ee
--- /dev/null
+++ b/trunk/avahi-core/timeeventq.c
@@ -0,0 +1,227 @@
+/* $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 <assert.h>
+#include <stdlib.h>
+
+#include <avahi-common/timeval.h>
+#include <avahi-common/malloc.h>
+
+#include "timeeventq.h"
+#include "log.h"
+
+struct AvahiTimeEvent {
+ AvahiTimeEventQueue *queue;
+ AvahiPrioQueueNode *node;
+ struct timeval expiry;
+ struct timeval last_run;
+ AvahiTimeEventCallback callback;
+ void* userdata;
+};
+
+struct AvahiTimeEventQueue {
+ const AvahiPoll *poll_api;
+ AvahiPrioQueue *prioq;
+ AvahiTimeout *timeout;
+};
+
+static int compare(const void* _a, const void* _b) {
+ const AvahiTimeEvent *a = _a, *b = _b;
+ int ret;
+
+ if ((ret = avahi_timeval_compare(&a->expiry, &b->expiry)) != 0)
+ return ret;
+
+ /* If both exevents are scheduled for the same time, put the entry
+ * that has been run earlier the last time first. */
+ return avahi_timeval_compare(&a->last_run, &b->last_run);
+}
+
+static AvahiTimeEvent* time_event_queue_root(AvahiTimeEventQueue *q) {
+ assert(q);
+
+ return q->prioq->root ? q->prioq->root->data : NULL;
+}
+
+static void update_timeout(AvahiTimeEventQueue *q) {
+ AvahiTimeEvent *e;
+ assert(q);
+
+ if ((e = time_event_queue_root(q)))
+ q->poll_api->timeout_update(q->timeout, &e->expiry);
+ else
+ q->poll_api->timeout_update(q->timeout, NULL);
+}
+
+static void expiration_event(AVAHI_GCC_UNUSED AvahiTimeout *timeout, void *userdata) {
+ AvahiTimeEventQueue *q = userdata;
+ AvahiTimeEvent *e;
+
+ if ((e = time_event_queue_root(q))) {
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ /* Check if expired */
+ if (avahi_timeval_compare(&now, &e->expiry) >= 0) {
+
+ /* Make sure to move the entry away from the front */
+ e->last_run = now;
+ avahi_prio_queue_shuffle(q->prioq, e->node);
+
+ /* Run it */
+ assert(e->callback);
+ e->callback(e, e->userdata);
+
+ update_timeout(q);
+ return;
+ }
+ }
+
+ avahi_log_debug(__FILE__": Strange, expiration_event() called, but nothing really happened.");
+ update_timeout(q);
+}
+
+static void fix_expiry_time(AvahiTimeEvent *e) {
+ struct timeval now;
+ assert(e);
+
+ return; /*** DO WE REALLY NEED THIS? ***/
+
+ gettimeofday(&now, NULL);
+
+ if (avahi_timeval_compare(&now, &e->expiry) > 0)
+ e->expiry = now;
+}
+
+AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api) {
+ AvahiTimeEventQueue *q;
+
+ if (!(q = avahi_new(AvahiTimeEventQueue, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ goto oom;
+ }
+
+ q->poll_api = poll_api;
+
+ if (!(q->prioq = avahi_prio_queue_new(compare)))
+ goto oom;
+
+ if (!(q->timeout = poll_api->timeout_new(poll_api, NULL, expiration_event, q)))
+ goto oom;
+
+ return q;
+
+oom:
+
+ if (q) {
+ avahi_free(q);
+
+ if (q->prioq)
+ avahi_prio_queue_free(q->prioq);
+ }
+
+ return NULL;
+}
+
+void avahi_time_event_queue_free(AvahiTimeEventQueue *q) {
+ AvahiTimeEvent *e;
+
+ assert(q);
+
+ while ((e = time_event_queue_root(q)))
+ avahi_time_event_free(e);
+ avahi_prio_queue_free(q->prioq);
+
+ q->poll_api->timeout_free(q->timeout);
+
+ avahi_free(q);
+}
+
+AvahiTimeEvent* avahi_time_event_new(
+ AvahiTimeEventQueue *q,
+ const struct timeval *timeval,
+ AvahiTimeEventCallback callback,
+ void* userdata) {
+
+ AvahiTimeEvent *e;
+
+ assert(q);
+ assert(callback);
+ assert(userdata);
+
+ if (!(e = avahi_new(AvahiTimeEvent, 1))) {
+ avahi_log_error(__FILE__": Out of memory");
+ return NULL; /* OOM */
+ }
+
+ e->queue = q;
+ e->callback = callback;
+ e->userdata = userdata;
+
+ if (timeval)
+ e->expiry = *timeval;
+ else {
+ e->expiry.tv_sec = 0;
+ e->expiry.tv_usec = 0;
+ }
+
+ fix_expiry_time(e);
+
+ e->last_run.tv_sec = 0;
+ e->last_run.tv_usec = 0;
+
+ if (!(e->node = avahi_prio_queue_put(q->prioq, e))) {
+ avahi_free(e);
+ return NULL;
+ }
+
+ update_timeout(q);
+ return e;
+}
+
+void avahi_time_event_free(AvahiTimeEvent *e) {
+ AvahiTimeEventQueue *q;
+ assert(e);
+
+ q = e->queue;
+
+ avahi_prio_queue_remove(q->prioq, e->node);
+ avahi_free(e);
+
+ update_timeout(q);
+}
+
+void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval) {
+ assert(e);
+ assert(timeval);
+
+ e->expiry = *timeval;
+ fix_expiry_time(e);
+ avahi_prio_queue_shuffle(e->queue->prioq, e->node);
+
+ update_timeout(e->queue);
+}
+
diff --git a/trunk/avahi-core/timeeventq.h b/trunk/avahi-core/timeeventq.h
new file mode 100644
index 0000000..cdfa5e6
--- /dev/null
+++ b/trunk/avahi-core/timeeventq.h
@@ -0,0 +1,48 @@
+#ifndef footimeeventqhfoo
+#define footimeeventqhfoo
+
+/* $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 <sys/types.h>
+
+typedef struct AvahiTimeEventQueue AvahiTimeEventQueue;
+typedef struct AvahiTimeEvent AvahiTimeEvent;
+
+#include <avahi-common/watch.h>
+
+#include "prioq.h"
+
+typedef void (*AvahiTimeEventCallback)(AvahiTimeEvent *e, void* userdata);
+
+AvahiTimeEventQueue* avahi_time_event_queue_new(const AvahiPoll *poll_api);
+void avahi_time_event_queue_free(AvahiTimeEventQueue *q);
+
+AvahiTimeEvent* avahi_time_event_new(
+ AvahiTimeEventQueue *q,
+ const struct timeval *timeval,
+ AvahiTimeEventCallback callback,
+ void* userdata);
+
+void avahi_time_event_free(AvahiTimeEvent *e);
+void avahi_time_event_update(AvahiTimeEvent *e, const struct timeval *timeval);
+
+#endif
diff --git a/trunk/avahi-core/update-test.c b/trunk/avahi-core/update-test.c
new file mode 100644
index 0000000..bdffc0c
--- /dev/null
+++ b/trunk/avahi-core/update-test.c
@@ -0,0 +1,92 @@
+/* $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 <assert.h>
+#include <stdlib.h>
+
+#include <avahi-common/error.h>
+#include <avahi-common/watch.h>
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/alternative.h>
+#include <avahi-common/timeval.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 void server_callback(AvahiServer *s, AvahiServerState state, AVAHI_GCC_UNUSED void* userdata) {
+
+ avahi_log_debug("server state: %i", state);
+
+ if (state == AVAHI_SERVER_RUNNING) {
+ int ret;
+
+ group = avahi_s_entry_group_new(s, NULL, NULL);
+ assert(group);
+
+ ret = avahi_server_add_service(s, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "foo", "_http._tcp", NULL, NULL, 80, "test1", NULL);
+ assert(ret == AVAHI_OK);
+
+ avahi_s_entry_group_commit(group);
+ }
+}
+
+static void modify_txt_callback(AVAHI_GCC_UNUSED AvahiTimeout *e, void *userdata) {
+ int ret;
+ AvahiServer *s = userdata;
+
+ avahi_log_debug("modifying");
+
+ ret = avahi_server_update_service_txt(s, group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "foo", "_http._tcp", NULL, "test2", NULL);
+ assert(ret == AVAHI_OK);
+}
+
+int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) {
+ AvahiSimplePoll *simple_poll;
+ const AvahiPoll *poll_api;
+ AvahiServer *server;
+ 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_domain = config.publish_workstation = config.use_ipv6 = config.publish_hinfo = 0;
+ server = avahi_server_new(poll_api, &config, server_callback, NULL, NULL);
+ assert(server);
+ avahi_server_config_free(&config);
+
+ poll_api->timeout_new(poll_api, avahi_elapse_time(&tv, 1000*10, 0), modify_txt_callback, server);
+
+ avahi_simple_poll_loop(simple_poll);
+}
diff --git a/trunk/avahi-core/util.c b/trunk/avahi-core/util.c
new file mode 100644
index 0000000..b1925f0
--- /dev/null
+++ b/trunk/avahi-core/util.c
@@ -0,0 +1,122 @@
+/* $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 <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include <avahi-common/malloc.h>
+#include "util.h"
+
+void avahi_hexdump(const void* p, size_t size) {
+ const uint8_t *c = p;
+ assert(p);
+
+ printf("Dumping %lu bytes from %p:\n", (unsigned long) size, p);
+
+ while (size > 0) {
+ unsigned i;
+
+ for (i = 0; i < 16; i++) {
+ if (i < size)
+ printf("%02x ", c[i]);
+ else
+ printf(" ");
+ }
+
+ for (i = 0; i < 16; i++) {
+ if (i < size)
+ printf("%c", c[i] >= 32 && c[i] < 127 ? c[i] : '.');
+ else
+ printf(" ");
+ }
+
+ printf("\n");
+
+ c += 16;
+
+ if (size <= 16)
+ break;
+
+ size -= 16;
+ }
+}
+
+char *avahi_format_mac_address(char *r, size_t l, const uint8_t* mac, size_t size) {
+ char *t = r;
+ unsigned i;
+ static const char hex[] = "0123456789abcdef";
+
+ assert(r);
+ assert(l > 0);
+ assert(mac);
+
+ if (size <= 0) {
+ *r = 0;
+ return r;
+ }
+
+ for (i = 0; i < size; i++) {
+ if (l < 3)
+ break;
+
+ *(t++) = hex[*mac >> 4];
+ *(t++) = hex[*mac & 0xF];
+ *(t++) = ':';
+
+ l -= 3;
+
+ mac++;
+ }
+
+ if (t > r)
+ *(t-1) = 0;
+ else
+ *r = 0;
+
+ return r;
+}
+
+char *avahi_strup(char *s) {
+ char *c;
+ assert(s);
+
+ for (c = s; *c; c++)
+ *c = (char) toupper(*c);
+
+ return s;
+}
+
+char *avahi_strdown(char *s) {
+ char *c;
+ assert(s);
+
+ for (c = s; *c; c++)
+ *c = (char) tolower(*c);
+
+ return s;
+}
diff --git a/trunk/avahi-core/util.h b/trunk/avahi-core/util.h
new file mode 100644
index 0000000..8d2caee
--- /dev/null
+++ b/trunk/avahi-core/util.h
@@ -0,0 +1,43 @@
+#ifndef fooutilhfoo
+#define fooutilhfoo
+
+/* $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 <inttypes.h>
+
+#include <avahi-common/cdecl.h>
+
+AVAHI_C_DECL_BEGIN
+
+void avahi_hexdump(const void *p, size_t size);
+
+char *avahi_format_mac_address(char *t, size_t l, const uint8_t* mac, size_t size);
+
+/** Change every character in the string to upper case (ASCII), return a pointer to the string */
+char *avahi_strup(char *s);
+
+/** Change every character in the string to lower case (ASCII), return a pointer to the string */
+char *avahi_strdown(char *s);
+
+AVAHI_C_DECL_END
+
+#endif
diff --git a/trunk/avahi-core/wide-area.c b/trunk/avahi-core/wide-area.c
new file mode 100644
index 0000000..4072f83
--- /dev/null
+++ b/trunk/avahi-core/wide-area.c
@@ -0,0 +1,725 @@
+/* $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 <netinet/in.h>
+
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+#include <avahi-common/timeval.h>
+
+#include "internal.h"
+#include "browse.h"
+#include "socket.h"
+#include "log.h"
+#include "hashmap.h"
+#include "wide-area.h"
+#include "addr-util.h"
+#include "rr-util.h"
+
+#define CACHE_ENTRIES_MAX 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_WIDE_AREA_SERVERS_MAX];
+ 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(AvahiWideAreaLookup *l, AvahiDnsPacket *p) {
+ AvahiAddress *a;
+
+ assert(l);
+ assert(p);
+
+ if (l->engine->n_dns_servers <= 0)
+ return -1;
+
+ assert(l->engine->current_dns_server < l->engine->n_dns_servers);
+
+ a = &l->engine->dns_servers[l->engine->current_dns_server];
+ l->dns_server_used = *a;
+
+ if (a->proto == AVAHI_PROTO_INET) {
+
+ if (l->engine->fd_ipv4 < 0)
+ return -1;
+
+ return avahi_send_dns_packet_ipv4(l->engine->fd_ipv4, AVAHI_IF_UNSPEC, p, NULL, &a->data.ipv4, AVAHI_DNS_PORT);
+
+ } else {
+ assert(a->proto == AVAHI_PROTO_INET6);
+
+ if (l->engine->fd_ipv6 < 0)
+ return -1;
+
+ return avahi_send_dns_packet_ipv6(l->engine->fd_ipv6, AVAHI_IF_UNSPEC, p, NULL, &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 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 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.");
+ avahi_server_set_errno(l->engine->server, AVAHI_ERR_TIMEOUT);
+ l->callback(l->engine, AVAHI_BROWSER_FAILURE, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata);
+ lookup_stop(l);
+ return;
+ }
+
+ assert(l->packet);
+ send_to_dns_server(l, 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(l, 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_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_RESULT_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_RESULT_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 >= CACHE_ENTRIES_MAX)
+ /* 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 int map_dns_error(uint16_t error) {
+ static const int table[16] = {
+ AVAHI_OK,
+ AVAHI_ERR_DNS_FORMERR,
+ AVAHI_ERR_DNS_SERVFAIL,
+ AVAHI_ERR_DNS_NXDOMAIN,
+ AVAHI_ERR_DNS_NOTIMP,
+ AVAHI_ERR_DNS_REFUSED,
+ AVAHI_ERR_DNS_YXDOMAIN,
+ AVAHI_ERR_DNS_YXRRSET,
+ AVAHI_ERR_DNS_NXRRSET,
+ AVAHI_ERR_DNS_NOTAUTH,
+ AVAHI_ERR_DNS_NOTZONE,
+ AVAHI_ERR_INVALID_DNS_ERROR,
+ AVAHI_ERR_INVALID_DNS_ERROR,
+ AVAHI_ERR_INVALID_DNS_ERROR,
+ AVAHI_ERR_INVALID_DNS_ERROR,
+ AVAHI_ERR_INVALID_DNS_ERROR
+ };
+
+ assert(error <= 15);
+
+ return table[error];
+}
+
+static void handle_packet(AvahiWideAreaLookupEngine *e, AvahiDnsPacket *p) {
+ AvahiWideAreaLookup *l = NULL;
+ int i, r;
+
+ AvahiBrowserEvent final_event = AVAHI_BROWSER_ALL_FOR_NOW;
+
+ assert(e);
+ assert(p);
+
+ /* Some superficial validity tests */
+ 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;
+ }
+
+ /* Look for the lookup that issued this query */
+ if (!(l = find_lookup(e, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ID))) || l->dead)
+ goto finish;
+
+ /* Check whether this a packet indicating a failure */
+ if ((r = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & 15) != 0 ||
+ avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
+
+ avahi_server_set_errno(e->server, r == 0 ? AVAHI_ERR_NOT_FOUND : map_dns_error(r));
+ /* Tell the user about the failure */
+ final_event = AVAHI_BROWSER_FAILURE;
+
+ /* We go on here, since some of the records contained in the
+ reply might be interesting in some way */
+ }
+
+ /* 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 or invalid while reading question key. (Maybe an UTF8 problem?)");
+ avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET);
+ final_event = AVAHI_BROWSER_FAILURE;
+ goto finish;
+ }
+
+ avahi_key_unref(k);
+ }
+
+ /* Process responses */
+ 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 or invalid while reading response ecord. (Maybe an UTF8 problem?)");
+ avahi_server_set_errno(e->server, AVAHI_ERR_INVALID_PACKET);
+ final_event = AVAHI_BROWSER_FAILURE;
+ goto finish;
+ }
+
+ add_to_cache(e, rr);
+ avahi_record_unref(rr);
+ }
+
+finish:
+
+ if (l && !l->dead) {
+ if (l->callback)
+ l->callback(e, final_event, AVAHI_LOOKUP_RESULT_WIDE_AREA, NULL, l->userdata);
+
+ lookup_stop(l);
+ }
+}
+
+static void socket_event(AVAHI_GCC_UNUSED AvahiWatch *w, int fd, AVAHI_GCC_UNUSED AvahiWatchEvent events, void *userdata) {
+ AvahiWideAreaLookupEngine *e = userdata;
+ AvahiDnsPacket *p = NULL;
+
+ if (fd == e->fd_ipv4)
+ p = avahi_recv_dns_packet_ipv4(e->fd_ipv4, NULL, NULL, NULL, NULL, NULL);
+ else {
+ assert(fd == e->fd_ipv6);
+ p = avahi_recv_dns_packet_ipv6(e->fd_ipv6, NULL, NULL, NULL, NULL, NULL);
+ }
+
+ if (p) {
+ handle_packet(e, p);
+ 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", 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 */
+
+ e->watch_ipv4 = e->watch_ipv6 = NULL;
+
+ 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_WIDE_AREA_SERVERS_MAX; 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_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_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_RESULT_WIDE_AREA|AVAHI_LOOKUP_RESULT_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/trunk/avahi-core/wide-area.h b/trunk/avahi-core/wide-area.h
new file mode 100644
index 0000000..1af613b
--- /dev/null
+++ b/trunk/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
+