summaryrefslogtreecommitdiffstats
path: root/avahi-core
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2005-05-07 13:37:03 +0000
committerLennart Poettering <lennart@poettering.net>2005-05-07 13:37:03 +0000
commitc58379bde376cb2298fca14f83a86626f1b76f2f (patch)
tree976f81b6e3530f29187ce0c017da9c91e584af06 /avahi-core
parente2c8b4dac0fefea406db0ec1a3b1c0861557601e (diff)
rename libavahi-core to avahi-core
git-svn-id: file:///home/lennart/svn/public/avahi/trunk@57 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe
Diffstat (limited to 'avahi-core')
-rw-r--r--avahi-core/Makefile.am88
-rw-r--r--avahi-core/address.c151
-rw-r--r--avahi-core/address.h60
-rw-r--r--avahi-core/announce.c422
-rw-r--r--avahi-core/announce.h72
-rw-r--r--avahi-core/avahi-test.c99
-rw-r--r--avahi-core/avahi.h164
-rw-r--r--avahi-core/cache.c372
-rw-r--r--avahi-core/cache.h87
-rw-r--r--avahi-core/dns-test.c60
-rw-r--r--avahi-core/dns.c772
-rw-r--r--avahi-core/dns.h103
-rw-r--r--avahi-core/domain-test.c44
-rw-r--r--avahi-core/iface.c586
-rw-r--r--avahi-core/iface.h123
-rw-r--r--avahi-core/llist.h71
-rw-r--r--avahi-core/netlink.c187
-rw-r--r--avahi-core/netlink.h41
-rw-r--r--avahi-core/prioq-test.c116
-rw-r--r--avahi-core/prioq.c384
-rw-r--r--avahi-core/prioq.h56
-rw-r--r--avahi-core/psched.c737
-rw-r--r--avahi-core/psched.h99
-rw-r--r--avahi-core/rr.c581
-rw-r--r--avahi-core/rr.h138
-rw-r--r--avahi-core/server.c1035
-rw-r--r--avahi-core/server.h100
-rw-r--r--avahi-core/socket.c511
-rw-r--r--avahi-core/socket.h46
-rw-r--r--avahi-core/strlst-test.c83
-rw-r--r--avahi-core/strlst.c243
-rw-r--r--avahi-core/strlst.h55
-rw-r--r--avahi-core/subscribe.c158
-rw-r--r--avahi-core/subscribe.h52
-rw-r--r--avahi-core/timeeventq.c185
-rw-r--r--avahi-core/timeeventq.h57
-rw-r--r--avahi-core/util.c239
-rw-r--r--avahi-core/util.h47
38 files changed, 8424 insertions, 0 deletions
diff --git a/avahi-core/Makefile.am b/avahi-core/Makefile.am
new file mode 100644
index 0000000..31268e8
--- /dev/null
+++ b/avahi-core/Makefile.am
@@ -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 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) -D_GNU_SOURCE
+
+# GLIB 2.0
+AM_CFLAGS+=$(GLIB20_CFLAGS)
+AM_LDADD=$(GLIB20_LIBS)
+
+# This cool debug trap works on i386/gcc only
+AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")'
+
+lib_LTLIBRARIES = \
+ libavahi-core.la
+
+noinst_PROGRAMS = \
+ dns-test \
+ domain-test \
+ prioq-test \
+ strlst-test \
+ avahi-test
+
+libavahi_core_la_SOURCES = \
+ timeeventq.c timeeventq.h\
+ iface.c iface.h \
+ netlink.c netlink.h \
+ server.c server.h \
+ address.c address.h \
+ util.c util.h \
+ prioq.c prioq.h \
+ cache.c cache.h\
+ rr.c rr.h \
+ dns.c dns.h \
+ socket.c socket.h \
+ psched.c psched.h \
+ announce.c announce.h \
+ subscribe.c subscribe.h \
+ strlst.c strlst.h
+
+prioq_test_SOURCES = \
+ prioq-test.c \
+ prioq.c prioq.h
+prioq_test_CFLAGS = $(AM_CFLAGS)
+prioq_test_LDADD = $(AM_LDADD)
+
+strlst_test_SOURCES = \
+ strlst-test.c \
+ strlst.c strlst.h
+strlst_test_CFLAGS = $(AM_CFLAGS)
+strlst_test_LDADD = $(AM_LDADD)
+
+domain_test_SOURCES = \
+ domain-test.c \
+ util.c util.h
+domain_test_CFLAGS = $(AM_CFLAGS)
+domain_test_LDADD = $(AM_LDADD)
+
+dns_test_SOURCES = \
+ dns-test.c \
+ util.c util.h \
+ dns.c dns.h \
+ rr.c rr.h \
+ strlst.c strlst.h
+dns_test_CFLAGS = $(AM_CFLAGS)
+dns_test_LDADD = $(AM_LDADD)
+
+avahi_test_SOURCES = \
+ avahi-test.c
+avahi_test_CFLAGS = $(AM_CFLAGS)
+avahi_test_LDADD = $(AM_LDADD) libavahi-core.la
+
+
diff --git a/avahi-core/address.c b/avahi-core/address.c
new file mode 100644
index 0000000..34f8581
--- /dev/null
+++ b/avahi-core/address.c
@@ -0,0 +1,151 @@
+/* $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 <arpa/inet.h>
+#include <string.h>
+
+#include "address.h"
+
+guint avahi_address_get_size(const AvahiAddress *a) {
+ g_assert(a);
+
+ if (a->family == AF_INET)
+ return 4;
+ else if (a->family == AF_INET6)
+ return 16;
+
+ return 0;
+}
+
+gint avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b) {
+ g_assert(a);
+ g_assert(b);
+
+ if (a->family != b->family)
+ return -1;
+
+ return memcmp(a->data.data, b->data.data, avahi_address_get_size(a));
+}
+
+gchar *avahi_address_snprint(char *s, guint length, const AvahiAddress *a) {
+ g_assert(s);
+ g_assert(length);
+ g_assert(a);
+ return (gchar*) inet_ntop(a->family, a->data.data, s, length);
+}
+
+gchar* avahi_reverse_lookup_name_ipv4(const AvahiIPv4Address *a) {
+ guint32 n = ntohl(a->address);
+ g_assert(a);
+
+ return g_strdup_printf("%u.%u.%u.%u.in-addr.arpa", n & 0xFF, (n >> 8) & 0xFF, (n >> 16) & 0xFF, n >> 24);
+}
+
+static gchar *reverse_lookup_name_ipv6(const AvahiIPv6Address *a, const gchar *suffix) {
+
+ return g_strdup_printf("%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%s",
+ a->address[15] & 0xF,
+ a->address[15] >> 4,
+ a->address[14] & 0xF,
+ a->address[14] >> 4,
+ a->address[13] & 0xF,
+ a->address[13] >> 4,
+ a->address[12] & 0xF,
+ a->address[12] >> 4,
+ a->address[11] & 0xF,
+ a->address[11] >> 4,
+ a->address[10] & 0xF,
+ a->address[10] >> 4,
+ a->address[9] & 0xF,
+ a->address[9] >> 4,
+ a->address[8] & 0xF,
+ a->address[8] >> 4,
+ a->address[7] & 0xF,
+ a->address[7] >> 4,
+ a->address[6] & 0xF,
+ a->address[6] >> 4,
+ a->address[5] & 0xF,
+ a->address[5] >> 4,
+ a->address[4] & 0xF,
+ a->address[4] >> 4,
+ a->address[3] & 0xF,
+ a->address[3] >> 4,
+ a->address[2] & 0xF,
+ a->address[2] >> 4,
+ a->address[1] & 0xF,
+ a->address[1] >> 4,
+ a->address[0] & 0xF,
+ a->address[0] >> 4,
+ suffix);
+}
+
+gchar *avahi_reverse_lookup_name_ipv6_arpa(const AvahiIPv6Address *a) {
+ return reverse_lookup_name_ipv6(a, "ip6.arpa");
+}
+
+gchar *avahi_reverse_lookup_name_ipv6_int(const AvahiIPv6Address *a) {
+ return reverse_lookup_name_ipv6(a, "ip6.int");
+}
+
+AvahiAddress *avahi_address_parse(const char *s, guchar family, AvahiAddress *ret_addr) {
+ g_assert(ret_addr);
+ g_assert(s);
+
+ if (inet_pton(family, s, ret_addr->data.data) < 0)
+ return NULL;
+
+ ret_addr->family = family;
+
+ return ret_addr;
+}
+
+AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr) {
+ g_assert(sa);
+ g_assert(ret_addr);
+
+ g_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+
+ ret_addr->family = sa->sa_family;
+
+ if (sa->sa_family == AF_INET)
+ memcpy(&ret_addr->data.ipv4, &((struct sockaddr_in*) sa)->sin_addr, sizeof(ret_addr->data.ipv4));
+ else
+ memcpy(&ret_addr->data.ipv6, &((struct sockaddr_in6*) sa)->sin6_addr, sizeof(ret_addr->data.ipv6));
+
+ return ret_addr;
+}
+
+guint16 avahi_port_from_sockaddr(const struct sockaddr* sa) {
+ g_assert(sa);
+
+ g_assert(sa->sa_family == AF_INET || sa->sa_family == AF_INET6);
+
+ if (sa->sa_family == AF_INET)
+ return ntohs(((struct sockaddr_in*) sa)->sin_port);
+ else
+ return ntohs(((struct sockaddr_in6*) sa)->sin6_port);
+}
diff --git a/avahi-core/address.h b/avahi-core/address.h
new file mode 100644
index 0000000..b88aba2
--- /dev/null
+++ b/avahi-core/address.h
@@ -0,0 +1,60 @@
+#ifndef fooaddresshfoo
+#define fooaddresshfoo
+
+/* $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 <glib.h>
+
+typedef struct {
+ guint32 address;
+} AvahiIPv4Address;
+
+typedef struct {
+ guint8 address[16];
+} AvahiIPv6Address;
+
+typedef struct {
+ guchar family;
+
+ union {
+ AvahiIPv6Address ipv6;
+ AvahiIPv4Address ipv4;
+ guint8 data[1];
+ } data;
+} AvahiAddress;
+
+guint avahi_address_get_size(const AvahiAddress *a);
+gint avahi_address_cmp(const AvahiAddress *a, const AvahiAddress *b);
+
+gchar *avahi_address_snprint(char *ret_s, guint length, const AvahiAddress *a);
+
+AvahiAddress *avahi_address_parse(const char *s, guchar family, AvahiAddress *ret_addr);
+
+AvahiAddress *avahi_address_from_sockaddr(const struct sockaddr* sa, AvahiAddress *ret_addr);
+guint16 avahi_port_from_sockaddr(const struct sockaddr* sa);
+
+gchar* avahi_reverse_lookup_name_ipv4(const AvahiIPv4Address *a);
+gchar* avahi_reverse_lookup_name_ipv6_arpa(const AvahiIPv6Address *a);
+gchar* avahi_reverse_lookup_name_ipv6_int(const AvahiIPv6Address *a);
+
+#endif
diff --git a/avahi-core/announce.c b/avahi-core/announce.c
new file mode 100644
index 0000000..dbad6c7
--- /dev/null
+++ b/avahi-core/announce.c
@@ -0,0 +1,422 @@
+/* $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 "announce.h"
+#include "util.h"
+
+#define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250
+#define AVAHI_PROBE_JITTER_MSEC 250
+#define AVAHI_PROBE_INTERVAL_MSEC 250
+
+static void remove_announcement(AvahiServer *s, AvahiAnnouncement *a) {
+ g_assert(s);
+ g_assert(a);
+
+ if (a->time_event)
+ avahi_time_event_queue_remove(s->time_event_queue, a->time_event);
+
+ AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_interface, a->interface->announcements, a);
+ AVAHI_LLIST_REMOVE(AvahiAnnouncement, by_entry, a->entry->announcements, a);
+
+ g_free(a);
+}
+
+static void elapse_announce(AvahiTimeEvent *e, void *userdata);
+
+static void set_timeout(AvahiAnnouncement *a, const GTimeVal *tv) {
+ g_assert(a);
+
+ if (!tv) {
+ if (a->time_event) {
+ avahi_time_event_queue_remove(a->server->time_event_queue, a->time_event);
+ a->time_event = NULL;
+ }
+ } else {
+
+ if (a->time_event)
+ avahi_time_event_queue_update(a->server->time_event_queue, a->time_event, tv);
+ else
+ a->time_event = avahi_time_event_queue_add(a->server->time_event_queue, tv, elapse_announce, a);
+ }
+}
+
+static void next_state(AvahiAnnouncement *a);
+
+void avahi_entry_group_check_probed(AvahiEntryGroup *g, gboolean immediately) {
+ AvahiEntry *e;
+ g_assert(g);
+ 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_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED);
+
+ if (g->dead)
+ return;
+
+ for (e = g->entries; e; e = e->entries_next) {
+ AvahiAnnouncement *a;
+
+ for (a = e->announcements; 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 {
+ GTimeVal tv;
+ a->n_iteration = 0;
+ avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
+ set_timeout(a, &tv);
+ }
+ }
+ }
+}
+
+static void next_state(AvahiAnnouncement *a) {
+ g_assert(a);
+
+/* g_message("%i -- %u", a->state, a->n_iteration); */
+
+ if (a->state == AVAHI_WAITING) {
+
+ g_assert(a->entry->group);
+
+ avahi_entry_group_check_probed(a->entry->group, TRUE);
+
+ } else if (a->state == AVAHI_PROBING) {
+
+ if (a->n_iteration >= 4) {
+ /* Probing done */
+
+ gchar *t;
+
+ g_message("Enough probes for record [%s]", t = avahi_record_to_string(a->entry->record));
+ g_free(t);
+
+ if (a->entry->group) {
+ g_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 {
+ GTimeVal tv;
+
+ avahi_interface_post_probe(a->interface, a->entry->record, FALSE);
+
+ avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0);
+ set_timeout(a, &tv);
+
+ a->n_iteration++;
+ }
+
+ } else if (a->state == AVAHI_ANNOUNCING) {
+
+ avahi_interface_post_response(a->interface, NULL, a->entry->record, a->entry->flags & AVAHI_ENTRY_UNIQUE, FALSE);
+
+ if (++a->n_iteration >= 4) {
+ gchar *t;
+ /* Announcing done */
+
+ g_message("Enough announcements for record [%s]", t = avahi_record_to_string(a->entry->record));
+ g_free(t);
+
+ a->state = AVAHI_ESTABLISHED;
+
+ set_timeout(a, NULL);
+ } else {
+ GTimeVal 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) {
+ g_assert(e);
+
+ next_state(userdata);
+}
+
+AvahiAnnouncement *avahi_get_announcement(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
+ AvahiAnnouncement *a;
+
+ g_assert(s);
+ g_assert(e);
+ g_assert(i);
+
+ for (a = e->announcements; a; a = a->by_entry_next)
+ if (a->interface == i)
+ return a;
+
+ return NULL;
+}
+
+static void new_announcement(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) {
+ AvahiAnnouncement *a;
+ GTimeVal tv;
+ gchar *t;
+
+ g_assert(s);
+ g_assert(i);
+ g_assert(e);
+ g_assert(!e->dead);
+
+/* g_message("NEW ANNOUNCEMENT: %s.%i [%s]", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record)); */
+/* g_free(t); */
+
+ if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_commited(e))
+ return;
+
+ /* We don't want duplicate announcements */
+ if (avahi_get_announcement(s, e, i))
+ return;
+
+ a = g_new(AvahiAnnouncement, 1);
+ a->server = s;
+ a->interface = i;
+ a->entry = e;
+
+ if ((e->flags & AVAHI_ENTRY_UNIQUE) && !(e->flags & AVAHI_ENTRY_NOPROBE))
+ a->state = AVAHI_PROBING;
+ else if (!(e->flags & AVAHI_ENTRY_NOANNOUNCE)) {
+
+ if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED)
+ a->state = AVAHI_ANNOUNCING;
+ else
+ a->state = AVAHI_WAITING;
+
+ } else
+ a->state = AVAHI_ESTABLISHED;
+
+
+ g_message("New announcement on interface %s.%i for entry [%s] state=%i", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record), a->state);
+ g_free(t);
+
+ a->n_iteration = 1;
+ a->sec_delay = 1;
+ a->time_event = NULL;
+
+ if (a->state == AVAHI_PROBING)
+ if (e->group)
+ e->group->n_probing++;
+
+ AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_interface, i->announcements, a);
+ AVAHI_LLIST_PREPEND(AvahiAnnouncement, by_entry, e->announcements, a);
+
+ if (a->state == AVAHI_PROBING) {
+ avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC);
+ set_timeout(a, &tv);
+ } else if (a->state == AVAHI_ANNOUNCING) {
+ avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
+ set_timeout(a, &tv);
+ }
+}
+
+void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) {
+ AvahiEntry *e;
+
+ g_assert(s);
+ g_assert(i);
+
+ if (!i->announcing)
+ return;
+
+ for (e = s->entries; e; e = e->entries_next)
+ if (!e->dead)
+ new_announcement(s, i, e);
+}
+
+static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
+ AvahiEntry *e = userdata;
+
+ g_assert(m);
+ g_assert(i);
+ g_assert(e);
+ g_assert(!e->dead);
+
+ new_announcement(m->server, i, e);
+}
+
+void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) {
+ g_assert(s);
+ g_assert(e);
+ g_assert(!e->dead);
+
+ avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e);
+}
+
+void avahi_announce_group(AvahiServer *s, AvahiEntryGroup *g) {
+ AvahiEntry *e;
+
+ g_assert(s);
+ g_assert(g);
+
+ for (e = g->entries; e; e = e->by_group_next)
+ if (!e->dead)
+ avahi_announce_entry(s, e);
+}
+
+gboolean avahi_entry_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
+ AvahiAnnouncement *a;
+
+ g_assert(s);
+ g_assert(e);
+ g_assert(i);
+ g_assert(!e->dead);
+
+ if (!(a = avahi_get_announcement(s, e, i)))
+ return FALSE;
+
+ return a->state == AVAHI_ANNOUNCING || a->state == AVAHI_ESTABLISHED;
+}
+
+gboolean avahi_entry_registering(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
+ AvahiAnnouncement *a;
+
+ g_assert(s);
+ g_assert(e);
+ g_assert(i);
+ g_assert(!e->dead);
+
+ if (!(a = avahi_get_announcement(s, e, i)))
+ return FALSE;
+
+ return a->state == AVAHI_PROBING || a->state == AVAHI_WAITING;
+}
+
+static AvahiRecord *make_goodbye_record(AvahiRecord *r) {
+/* gchar *t; */
+ AvahiRecord *g;
+
+ g_assert(r);
+
+/* g_message("Preparing goodbye for record [%s]", t = avahi_record_to_string(r)); */
+/* g_free(t); */
+
+ g = avahi_record_copy(r);
+ g_assert(g->ref == 1);
+ g->ttl = 0;
+
+ return g;
+}
+
+static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
+ AvahiEntry *e = userdata;
+ AvahiRecord *g;
+
+ g_assert(m);
+ g_assert(i);
+ g_assert(e);
+ g_assert(!e->dead);
+
+ if (!avahi_interface_match(i, e->interface, e->protocol))
+ return;
+
+ if (e->flags & AVAHI_ENTRY_NOANNOUNCE)
+ return;
+
+ if (!avahi_entry_registered(m->server, e, i))
+ return;
+
+ g = make_goodbye_record(e->record);
+ avahi_interface_post_response(i, NULL, g, e->flags & AVAHI_ENTRY_UNIQUE, TRUE);
+ avahi_record_unref(g);
+}
+
+void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, gboolean goodbye) {
+ g_assert(s);
+ g_assert(i);
+
+/* g_message("goodbye interface: %s.%u", i->hardware->name, i->protocol); */
+
+ if (goodbye && avahi_interface_relevant(i)) {
+ AvahiEntry *e;
+
+ for (e = s->entries; e; e = e->entries_next)
+ if (!e->dead)
+ send_goodbye_callback(s->monitor, i, e);
+ }
+
+ while (i->announcements)
+ remove_announcement(s, i->announcements);
+
+/* g_message("goodbye interface done: %s.%u", i->hardware->name, i->protocol); */
+
+}
+
+void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, gboolean goodbye) {
+ g_assert(s);
+ g_assert(e);
+
+/* g_message("goodbye entry: %p", e); */
+
+ if (goodbye && !e->dead)
+ avahi_interface_monitor_walk(s->monitor, 0, AF_UNSPEC, send_goodbye_callback, e);
+
+ while (e->announcements)
+ remove_announcement(s, e->announcements);
+
+/* g_message("goodbye entry done: %p", e); */
+
+}
+
+void avahi_goodbye_all(AvahiServer *s, gboolean goodbye) {
+ AvahiEntry *e;
+
+ g_assert(s);
+
+/* g_message("goodbye all"); */
+
+ for (e = s->entries; e; e = e->entries_next)
+ if (!e->dead)
+ avahi_goodbye_entry(s, e, goodbye);
+
+/* g_message("goodbye all done"); */
+
+}
+
diff --git a/avahi-core/announce.h b/avahi-core/announce.h
new file mode 100644
index 0000000..71b2f03
--- /dev/null
+++ b/avahi-core/announce.h
@@ -0,0 +1,72 @@
+#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.
+***/
+
+#include <glib.h>
+
+typedef struct _AvahiAnnouncement AvahiAnnouncement;
+
+#include "llist.h"
+#include "iface.h"
+#include "server.h"
+#include "timeeventq.h"
+
+typedef enum {
+ AVAHI_PROBING,
+ AVAHI_WAITING, /* wait for other records in group */
+ AVAHI_ANNOUNCING,
+ AVAHI_ESTABLISHED
+} AvahiAnnouncementState;
+
+struct _AvahiAnnouncement {
+ AvahiServer *server;
+ AvahiInterface *interface;
+ AvahiEntry *entry;
+
+ AvahiTimeEvent *time_event;
+
+ AvahiAnnouncementState state;
+ guint n_iteration;
+ guint sec_delay;
+
+ AVAHI_LLIST_FIELDS(AvahiAnnouncement, by_interface);
+ AVAHI_LLIST_FIELDS(AvahiAnnouncement, by_entry);
+};
+
+void avahi_announce_interface(AvahiServer *s, AvahiInterface *i);
+void avahi_announce_entry(AvahiServer *s, AvahiEntry *e);
+void avahi_announce_group(AvahiServer *s, AvahiEntryGroup *g);
+
+void avahi_entry_group_check_probed(AvahiEntryGroup *g, gboolean immediately);
+
+gboolean avahi_entry_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i);
+gboolean avahi_entry_registering(AvahiServer *s, AvahiEntry *e, AvahiInterface *i);
+
+void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, gboolean send);
+void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, gboolean send);
+
+void avahi_goodbye_all(AvahiServer *s, gboolean send);
+
+AvahiAnnouncement *avahi_get_announcement(AvahiServer *s, AvahiEntry *e, AvahiInterface *i);
+
+#endif
diff --git a/avahi-core/avahi-test.c b/avahi-core/avahi-test.c
new file mode 100644
index 0000000..1de97a0
--- /dev/null
+++ b/avahi-core/avahi-test.c
@@ -0,0 +1,99 @@
+/* $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/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+
+#include "avahi.h"
+
+static gboolean quit_timeout(gpointer data) {
+ g_main_loop_quit(data);
+ return FALSE;
+}
+
+static gboolean dump_timeout(gpointer data) {
+ AvahiServer *Avahi = data;
+ avahi_server_dump(Avahi, stdout);
+ return TRUE;
+}
+
+static void subscription(AvahiSubscription *s, AvahiRecord *r, gint interface, guchar protocol, AvahiSubscriptionEvent event, gpointer userdata) {
+ gchar *t;
+
+ g_assert(s);
+ g_assert(r);
+ g_assert(interface > 0);
+ g_assert(protocol != AF_UNSPEC);
+
+ g_message("SUBSCRIPTION: record [%s] on %i.%i is %s", t = avahi_record_to_string(r), interface, protocol,
+ event == AVAHI_SUBSCRIPTION_NEW ? "new" : (event == AVAHI_SUBSCRIPTION_CHANGE ? "changed" : "removed"));
+
+ g_free(t);
+}
+
+static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata) {
+ g_message("entry group state: %i", state);
+}
+
+int main(int argc, char *argv[]) {
+ AvahiServer *avahi;
+ gchar *r;
+ GMainLoop *loop = NULL;
+ AvahiSubscription *s;
+ AvahiKey *k;
+ AvahiEntryGroup *g;
+
+ avahi = avahi_server_new(NULL);
+
+/* g = avahi_entry_group_new(avahi, entry_group_callback, NULL); */
+
+/* avahi_server_add_text(avahi, g, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, NULL, "hallo", NULL); */
+/* avahi_server_add_service(avahi, g, 0, AF_UNSPEC, "_http._tcp", "gurke", NULL, NULL, 80, "foo", NULL); */
+
+/* avahi_entry_group_commit(g); */
+
+ avahi_server_dump(avahi, stdout);
+
+
+/* k = avahi_key_new("ecstasy.local.", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_ANY); */
+/* s = avahi_subscription_new(avahi, k, 0, AF_UNSPEC, subscription, NULL); */
+/* avahi_key_unref(k); */
+
+ loop = g_main_loop_new(NULL, FALSE);
+
+ /* g_timeout_add(1000*20, dump_timeout, Avahi); */
+/* g_timeout_add(1000*30, quit_timeout, loop); */
+
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+
+/* avahi_subscription_free(s); */
+ /* avahi_entry_group_free(g); */
+ avahi_server_free(avahi);
+
+ return 0;
+}
diff --git a/avahi-core/avahi.h b/avahi-core/avahi.h
new file mode 100644
index 0000000..bbb7873
--- /dev/null
+++ b/avahi-core/avahi.h
@@ -0,0 +1,164 @@
+#ifndef fooavahihfoo
+#define fooavahihfoo
+
+/* $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 <stdio.h>
+#include <glib.h>
+
+typedef struct _AvahiServer AvahiServer;
+typedef struct _AvahiEntry AvahiEntry;
+typedef struct _AvahiEntryGroup AvahiEntryGroup;
+
+#include "address.h"
+#include "rr.h"
+
+typedef enum {
+ AVAHI_ENTRY_NULL = 0,
+ AVAHI_ENTRY_UNIQUE = 1,
+ AVAHI_ENTRY_NOPROBE = 2,
+ AVAHI_ENTRY_NOANNOUNCE = 4
+} AvahiEntryFlags;
+
+typedef enum {
+ AVAHI_ENTRY_GROUP_UNCOMMITED,
+ AVAHI_ENTRY_GROUP_REGISTERING,
+ AVAHI_ENTRY_GROUP_ESTABLISHED,
+ AVAHI_ENTRY_GROUP_COLLISION
+} AvahiEntryGroupState;
+
+typedef void (*AvahiEntryGroupCallback) (AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata);
+
+AvahiServer *avahi_server_new(GMainContext *c);
+void avahi_server_free(AvahiServer* s);
+
+const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state);
+void avahi_server_dump(AvahiServer *s, FILE *f);
+
+AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata);
+void avahi_entry_group_free(AvahiEntryGroup *g);
+void avahi_entry_group_commit(AvahiEntryGroup *g);
+AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g);
+
+void avahi_server_add(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ AvahiRecord *r);
+
+void avahi_server_add_ptr(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ const gchar *dest);
+
+void avahi_server_add_address(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ AvahiAddress *a);
+
+void avahi_server_add_text(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ ... /* text records, terminated by NULL */);
+
+void avahi_server_add_text_va(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ va_list va);
+
+void avahi_server_add_text_strlst(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ AvahiStringList *strlst);
+
+void avahi_server_add_service(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ const gchar *type,
+ const gchar *name,
+ const gchar *domain,
+ const gchar *host,
+ guint16 port,
+ ... /* text records, terminated by NULL */);
+
+void avahi_server_add_service_va(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ const gchar *type,
+ const gchar *name,
+ const gchar *domain,
+ const gchar *host,
+ guint16 port,
+ va_list va);
+
+void avahi_server_add_service_strlst(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ const gchar *type,
+ const gchar *name,
+ const gchar *domain,
+ const gchar *host,
+ guint16 port,
+ AvahiStringList *strlst);
+
+typedef enum {
+ AVAHI_SUBSCRIPTION_NEW,
+ AVAHI_SUBSCRIPTION_REMOVE,
+ AVAHI_SUBSCRIPTION_CHANGE
+} AvahiSubscriptionEvent;
+
+typedef struct _AvahiSubscription AvahiSubscription;
+
+typedef void (*AvahiSubscriptionCallback)(AvahiSubscription *s, AvahiRecord *record, gint interface, guchar protocol, AvahiSubscriptionEvent event, gpointer userdata);
+
+AvahiSubscription *avahi_subscription_new(AvahiServer *s, AvahiKey *key, gint interface, guchar protocol, AvahiSubscriptionCallback callback, gpointer userdata);
+void avahi_subscription_free(AvahiSubscription *s);
+
+#endif
diff --git a/avahi-core/cache.c b/avahi-core/cache.c
new file mode 100644
index 0000000..0307b8e
--- /dev/null
+++ b/avahi-core/cache.c
@@ -0,0 +1,372 @@
+/* $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 "util.h"
+#include "cache.h"
+
+static void remove_entry(AvahiCache *c, AvahiCacheEntry *e) {
+ AvahiCacheEntry *t;
+
+ g_assert(c);
+ g_assert(e);
+
+/* g_message("removing from cache: %p %p", c, e); */
+
+ /* Remove from hash table */
+ t = g_hash_table_lookup(c->hash_table, e->record->key);
+ AVAHI_LLIST_REMOVE(AvahiCacheEntry, by_key, t, e);
+ if (t)
+ g_hash_table_replace(c->hash_table, t->record->key, t);
+ else
+ g_hash_table_remove(c->hash_table, e->record->key);
+
+ /* Remove from linked list */
+ AVAHI_LLIST_REMOVE(AvahiCacheEntry, entry, c->entries, e);
+
+ if (e->time_event)
+ avahi_time_event_queue_remove(c->server->time_event_queue, e->time_event);
+
+ avahi_subscription_notify(c->server, c->interface, e->record, AVAHI_SUBSCRIPTION_REMOVE);
+
+ avahi_record_unref(e->record);
+
+ g_free(e);
+}
+
+AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *iface) {
+ AvahiCache *c;
+ g_assert(server);
+
+ c = g_new(AvahiCache, 1);
+ c->server = server;
+ c->interface = iface;
+ c->hash_table = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
+
+ AVAHI_LLIST_HEAD_INIT(AvahiCacheEntry, c->entries);
+
+ return c;
+}
+
+void avahi_cache_free(AvahiCache *c) {
+ g_assert(c);
+
+ while (c->entries)
+ remove_entry(c, c->entries);
+
+ g_hash_table_destroy(c->hash_table);
+
+ g_free(c);
+}
+
+AvahiCacheEntry *avahi_cache_lookup_key(AvahiCache *c, AvahiKey *k) {
+ g_assert(c);
+ g_assert(k);
+
+ g_assert(!avahi_key_is_pattern(k));
+
+ return g_hash_table_lookup(c->hash_table, k);
+}
+
+gpointer avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, gpointer userdata) {
+ gpointer ret;
+
+ g_assert(c);
+ g_assert(pattern);
+ g_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 = avahi_cache_lookup_key(c, pattern); e; e = n) {
+ n = e->by_key_next;
+
+ if ((ret = cb(c, pattern, e, userdata)))
+ return ret;
+ }
+ }
+
+ return NULL;
+}
+
+static gpointer lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) {
+ g_assert(c);
+ g_assert(pattern);
+ g_assert(e);
+
+ if (avahi_record_equal_no_ttl(e->record, userdata))
+ return e;
+
+ return NULL;
+}
+
+AvahiCacheEntry *avahi_cache_lookup_record(AvahiCache *c, AvahiRecord *r) {
+ g_assert(c);
+ g_assert(r);
+
+ return avahi_cache_walk(c, r->key, lookup_record_callback, r);
+}
+
+static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, guint percent);
+
+static void elapse_func(AvahiTimeEvent *t, void *userdata) {
+ AvahiCacheEntry *e = userdata;
+
+ g_assert(t);
+ g_assert(e);
+
+ if (e->state == AVAHI_CACHE_FINAL) {
+ remove_entry(e->cache, e);
+ g_message("Removing entry from cache due to expiration");
+ } else {
+ guint percent = 0;
+
+ switch (e->state) {
+ case AVAHI_CACHE_VALID:
+ 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_FINAL;
+ percent = 100;
+ break;
+
+ default:
+ ;
+ }
+
+ g_assert(percent > 0);
+
+ g_message("Requesting cache entry update at %i%%.", percent);
+
+ /* Request a cache update, if we are subscribed to this entry */
+ if (avahi_is_subscribed(e->cache->server, e->record->key))
+ avahi_interface_post_query(e->cache->interface, e->record->key, TRUE);
+
+ /* Check again later */
+ next_expiry(e->cache, e, percent);
+ }
+}
+
+static void update_time_event(AvahiCache *c, AvahiCacheEntry *e) {
+ g_assert(c);
+ g_assert(e);
+
+ if (e->time_event)
+ avahi_time_event_queue_update(c->server->time_event_queue, e->time_event, &e->expiry);
+ else
+ e->time_event = avahi_time_event_queue_add(c->server->time_event_queue, &e->expiry, elapse_func, e);
+}
+
+static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, guint percent) {
+ gulong usec;
+
+ g_assert(c);
+ g_assert(e);
+ g_assert(percent > 0 && percent <= 100);
+
+ e->expiry = e->timestamp;
+
+ usec = e->record->ttl * 10000;
+
+ /* 2% jitter */
+ usec = g_random_int_range(usec*percent, usec*(percent+2));
+
+ g_time_val_add(&e->expiry, usec);
+ update_time_event(c, e);
+}
+
+void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const AvahiAddress *a) {
+ AvahiCacheEntry *e, *t;
+ gchar *txt;
+
+ g_assert(c);
+ g_assert(r && r->ref >= 1);
+
+ g_message("cache update: %s", (txt = avahi_record_to_string(r)));
+ g_free(txt);
+
+ if (r->ttl == 0) {
+
+ /* This is a goodbye request */
+
+ if ((e = avahi_cache_lookup_record(c, r))) {
+
+ e->state = AVAHI_CACHE_FINAL;
+ g_get_current_time(&e->timestamp);
+ e->expiry = e->timestamp;
+ g_time_val_add(&e->expiry, 1000000); /* 1s */
+ update_time_event(c, e);
+ }
+
+ } else {
+
+ /* This is an update request */
+
+ if ((t = e = avahi_cache_lookup_key(c, r->key))) {
+
+ if (unique) {
+
+ /* For unique records, remove all entries but one */
+ while (e->by_key_next)
+ remove_entry(c, e->by_key_next);
+
+ } else {
+
+ /* For non-unique record, look for exactly the same entry */
+ for (; e; e = e->by_key_next)
+ if (avahi_record_equal_no_ttl(e->record, r))
+ break;
+ }
+ }
+
+ if (e) {
+
+/* g_message("found matching cache entry"); */
+
+ /* We are the first in the linked list so let's replace the hash table key with the new one */
+ if (e->by_key_prev == NULL)
+ g_hash_table_replace(c->hash_table, r->key, e);
+
+ /* Notify subscribers */
+ if (!avahi_record_equal_no_ttl(e->record, r))
+ avahi_subscription_notify(c->server, c->interface, r, AVAHI_SUBSCRIPTION_CHANGE);
+
+ /* Update the record */
+ avahi_record_unref(e->record);
+ e->record = avahi_record_ref(r);
+
+ } else {
+ /* No entry found, therefore we create a new one */
+
+/* g_message("couldn't find matching cache entry"); */
+
+ e = g_new(AvahiCacheEntry, 1);
+ e->cache = c;
+ e->time_event = NULL;
+ e->record = avahi_record_ref(r);
+
+ /* Append to hash table */
+ AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, t, e);
+ g_hash_table_replace(c->hash_table, e->record->key, t);
+
+ /* Append to linked list */
+ AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e);
+
+ /* Notify subscribers */
+ avahi_subscription_notify(c->server, c->interface, e->record, AVAHI_SUBSCRIPTION_NEW);
+ }
+
+ e->origin = *a;
+ g_get_current_time(&e->timestamp);
+ next_expiry(c, e, 80);
+ e->state = AVAHI_CACHE_VALID;
+ }
+}
+
+static gpointer drop_key_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
+ g_assert(c);
+ g_assert(pattern);
+ g_assert(e);
+
+ remove_entry(c, e);
+ return NULL;
+}
+
+void avahi_cache_drop_key(AvahiCache *c, AvahiKey *k) {
+ g_assert(c);
+ g_assert(k);
+
+ avahi_cache_walk(c, k, drop_key_callback, NULL);
+}
+
+void avahi_cache_drop_record(AvahiCache *c, AvahiRecord *r) {
+ AvahiCacheEntry *e;
+
+ g_assert(c);
+ g_assert(r);
+
+ if ((e = avahi_cache_lookup_record(c, r)))
+ remove_entry(c, e);
+}
+
+static void func(gpointer key, gpointer data, gpointer userdata) {
+ AvahiCacheEntry *e = data;
+ AvahiKey *k = key;
+ gchar *t;
+
+ g_assert(k);
+ g_assert(e);
+
+ t = avahi_record_to_string(e->record);
+ fprintf((FILE*) userdata, "%s\n", t);
+ g_free(t);
+}
+
+void avahi_cache_dump(AvahiCache *c, FILE *f) {
+ g_assert(c);
+ g_assert(f);
+
+ fprintf(f, ";;; CACHE DUMP FOLLOWS ;;;\n");
+ g_hash_table_foreach(c->hash_table, func, f);
+}
+
+gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) {
+ GTimeVal now;
+ guint age;
+
+ g_assert(c);
+ g_assert(e);
+
+ g_get_current_time(&now);
+
+ age = avahi_timeval_diff(&now, &e->timestamp)/1000000;
+
+ g_message("age: %u, ttl/2: %u", age, e->record->ttl);
+
+ return age >= e->record->ttl/2;
+}
diff --git a/avahi-core/cache.h b/avahi-core/cache.h
new file mode 100644
index 0000000..d64b3ee
--- /dev/null
+++ b/avahi-core/cache.h
@@ -0,0 +1,87 @@
+#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.
+***/
+
+#include <glib.h>
+
+struct _AvahiCache;
+typedef struct _AvahiCache AvahiCache;
+
+#include "prioq.h"
+#include "server.h"
+#include "llist.h"
+#include "timeeventq.h"
+
+typedef enum {
+ AVAHI_CACHE_VALID,
+ AVAHI_CACHE_EXPIRY1,
+ AVAHI_CACHE_EXPIRY2,
+ AVAHI_CACHE_EXPIRY3,
+ AVAHI_CACHE_FINAL
+} AvahiCacheEntryState;
+
+typedef struct AvahiCacheEntry AvahiCacheEntry;
+
+struct AvahiCacheEntry {
+ AvahiCache *cache;
+ AvahiRecord *record;
+ GTimeVal timestamp;
+ GTimeVal expiry;
+
+ AvahiAddress origin;
+
+ AvahiCacheEntryState state;
+ AvahiTimeEvent *time_event;
+
+ AVAHI_LLIST_FIELDS(AvahiCacheEntry, by_key);
+ AVAHI_LLIST_FIELDS(AvahiCacheEntry, entry);
+};
+
+struct _AvahiCache {
+ AvahiServer *server;
+
+ AvahiInterface *interface;
+
+ GHashTable *hash_table;
+
+ AVAHI_LLIST_HEAD(AvahiCacheEntry, entries);
+};
+
+AvahiCache *avahi_cache_new(AvahiServer *server, AvahiInterface *interface);
+void avahi_cache_free(AvahiCache *c);
+
+AvahiCacheEntry *avahi_cache_lookup_key(AvahiCache *c, AvahiKey *k);
+AvahiCacheEntry *avahi_cache_lookup_record(AvahiCache *c, AvahiRecord *r);
+
+void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const AvahiAddress *a);
+
+void avahi_cache_drop_record(AvahiCache *c, AvahiRecord *r);
+
+void avahi_cache_dump(AvahiCache *c, FILE *f);
+
+typedef gpointer AvahiCacheWalkCallback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata);
+gpointer avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallback cb, gpointer userdata);
+
+gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e);
+
+#endif
diff --git a/avahi-core/dns-test.c b/avahi-core/dns-test.c
new file mode 100644
index 0000000..e0680c2
--- /dev/null
+++ b/avahi-core/dns-test.c
@@ -0,0 +1,60 @@
+/* $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 "dns.h"
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+ gchar t[256], *a, *b, *c, *d;
+ AvahiDnsPacket *p;
+
+ p = avahi_dns_packet_new(8000);
+
+ avahi_dns_packet_append_name(p, a = "hello.hello.hello.de.");
+ avahi_dns_packet_append_name(p, b = "this is a test.hello.de.");
+ avahi_dns_packet_append_name(p, c = "this\\.is\\.a\\.test\\.with\\.dots.hello.de.");
+ avahi_dns_packet_append_name(p, d = "this\\\\is another\\ \\test.hello.de.");
+
+ avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), p->size);
+
+ avahi_dns_packet_consume_name(p, t, sizeof(t));
+ g_message(">%s<", t);
+ g_assert(avahi_domain_equal(a, t));
+
+ avahi_dns_packet_consume_name(p, t, sizeof(t));
+ g_message(">%s<", t);
+ g_assert(avahi_domain_equal(b, t));
+
+ avahi_dns_packet_consume_name(p, t, sizeof(t));
+ g_message(">%s<", t);
+ g_assert(avahi_domain_equal(c, t));
+
+ avahi_dns_packet_consume_name(p, t, sizeof(t));
+ g_message(">%s<", t);
+ g_assert(avahi_domain_equal(d, t));
+
+ avahi_dns_packet_free(p);
+ return 0;
+}
diff --git a/avahi-core/dns.c b/avahi-core/dns.c
new file mode 100644
index 0000000..cc1463a
--- /dev/null
+++ b/avahi-core/dns.c
@@ -0,0 +1,772 @@
+/* $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 <netinet/in.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "dns.h"
+#include "util.h"
+
+AvahiDnsPacket* avahi_dns_packet_new(guint max_size) {
+ AvahiDnsPacket *p;
+
+ if (max_size <= 0)
+ max_size = AVAHI_DNS_PACKET_MAX_SIZE;
+ else if (max_size < AVAHI_DNS_PACKET_HEADER_SIZE)
+ max_size = AVAHI_DNS_PACKET_HEADER_SIZE;
+
+ p = g_malloc(sizeof(AvahiDnsPacket) + max_size);
+ p->size = p->rindex = AVAHI_DNS_PACKET_HEADER_SIZE;
+ p->max_size = max_size;
+ p->name_table = NULL;
+
+ memset(AVAHI_DNS_PACKET_DATA(p), 0, p->size);
+ return p;
+}
+
+AvahiDnsPacket* avahi_dns_packet_new_query(guint max_size) {
+ AvahiDnsPacket *p;
+
+ p = avahi_dns_packet_new(max_size);
+ 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(guint max_size) {
+ AvahiDnsPacket *p;
+
+ p = avahi_dns_packet_new(max_size);
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, AVAHI_DNS_FLAGS(1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ return p;
+}
+
+void avahi_dns_packet_free(AvahiDnsPacket *p) {
+ g_assert(p);
+
+ if (p->name_table)
+ g_hash_table_destroy(p->name_table);
+
+ g_free(p);
+}
+
+void avahi_dns_packet_set_field(AvahiDnsPacket *p, guint index, guint16 v) {
+ g_assert(p);
+ g_assert(index < AVAHI_DNS_PACKET_HEADER_SIZE);
+
+ ((guint16*) AVAHI_DNS_PACKET_DATA(p))[index] = g_htons(v);
+}
+
+guint16 avahi_dns_packet_get_field(AvahiDnsPacket *p, guint index) {
+ g_assert(p);
+ g_assert(index < AVAHI_DNS_PACKET_HEADER_SIZE);
+
+ return g_ntohs(((guint16*) AVAHI_DNS_PACKET_DATA(p))[index]);
+}
+
+/* Read the first label from string *name, unescape "\" and write it to dest */
+gchar *avahi_unescape_label(gchar *dest, guint size, const gchar **name) {
+ guint i = 0;
+ gchar *d;
+
+ g_assert(dest);
+ g_assert(size > 0);
+ g_assert(name);
+ g_assert(*name);
+
+ d = dest;
+
+ for (;;) {
+ if (i >= size)
+ return NULL;
+
+ if (**name == '.') {
+ (*name)++;
+ break;
+ }
+
+ if (**name == 0)
+ break;
+
+ if (**name == '\\') {
+ (*name) ++;
+
+ if (**name == 0)
+ break;
+ }
+
+ *(d++) = *((*name) ++);
+ i++;
+ }
+
+ g_assert(i < size);
+
+ *d = 0;
+
+ return dest;
+}
+
+guint8* avahi_dns_packet_append_name(AvahiDnsPacket *p, const gchar *name) {
+ guint8 *d, *saved_ptr = NULL;
+ guint saved_size;
+
+ g_assert(p);
+ g_assert(name);
+
+ saved_size = p->size;
+ saved_ptr = avahi_dns_packet_extend(p, 0);
+
+ while (*name) {
+ guint8* prev;
+ const gchar *pname;
+ gchar label[64];
+
+ /* Check whether we can compress this name. */
+
+ if (p->name_table && (prev = g_hash_table_lookup(p->name_table, name))) {
+ guint index;
+
+ g_assert(prev >= AVAHI_DNS_PACKET_DATA(p));
+ index = (guint) (prev - AVAHI_DNS_PACKET_DATA(p));
+
+ g_assert(index < p->size);
+
+ if (index < 0x4000) {
+ guint16 *t;
+ if (!(t = (guint16*) avahi_dns_packet_extend(p, sizeof(guint16))))
+ return NULL;
+
+ *t = g_htons((0xC000 | index));
+ return saved_ptr;
+ }
+ }
+
+ pname = name;
+
+ if (!(avahi_unescape_label(label, sizeof(label), &name)))
+ goto fail;
+
+ if (!(d = avahi_dns_packet_append_string(p, label)))
+ goto fail;
+
+ if (!p->name_table)
+ p->name_table = g_hash_table_new_full((GHashFunc) avahi_domain_hash, (GEqualFunc) avahi_domain_equal, g_free, NULL);
+
+ g_hash_table_insert(p->name_table, g_strdup(pname), d);
+ }
+
+ if (!(d = avahi_dns_packet_extend(p, 1)))
+ goto fail;
+
+ *d = 0;
+
+ return saved_ptr;
+
+fail:
+ p->size = saved_size;
+ return NULL;
+}
+
+guint8* avahi_dns_packet_append_uint16(AvahiDnsPacket *p, guint16 v) {
+ guint8 *d;
+ g_assert(p);
+
+ if (!(d = avahi_dns_packet_extend(p, sizeof(guint16))))
+ return NULL;
+
+ *((guint16*) d) = g_htons(v);
+ return d;
+}
+
+guint8 *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, guint32 v) {
+ guint8 *d;
+ g_assert(p);
+
+ if (!(d = avahi_dns_packet_extend(p, sizeof(guint32))))
+ return NULL;
+
+ *((guint32*) d) = g_htonl(v);
+
+ return d;
+}
+
+guint8 *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, gconstpointer b, guint l) {
+ guint8* d;
+
+ g_assert(p);
+ g_assert(b);
+ g_assert(l);
+
+ if (!(d = avahi_dns_packet_extend(p, l)))
+ return NULL;
+
+ memcpy(d, b, l);
+ return d;
+}
+
+guint8* avahi_dns_packet_append_string(AvahiDnsPacket *p, const gchar *s) {
+ guint8* d;
+ guint k;
+
+ g_assert(p);
+ g_assert(s);
+
+ if ((k = strlen(s)) >= 255)
+ k = 255;
+
+ if (!(d = avahi_dns_packet_extend(p, k+1)))
+ return NULL;
+
+ *d = (guint8) k;
+ memcpy(d+1, s, k);
+
+ return d;
+}
+
+guint8 *avahi_dns_packet_extend(AvahiDnsPacket *p, guint l) {
+ guint8 *d;
+
+ g_assert(p);
+
+ if (p->size+l > p->max_size)
+ return NULL;
+
+ d = AVAHI_DNS_PACKET_DATA(p) + p->size;
+ p->size += l;
+
+ return d;
+}
+
+gint avahi_dns_packet_check_valid(AvahiDnsPacket *p) {
+ guint16 flags;
+ g_assert(p);
+
+ if (p->size < 12)
+ return -1;
+
+ flags = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS);
+
+ if (flags & AVAHI_DNS_FLAG_OPCODE || flags & AVAHI_DNS_FLAG_RCODE)
+ return -1;
+
+ return 0;
+}
+
+gint avahi_dns_packet_is_query(AvahiDnsPacket *p) {
+ g_assert(p);
+
+ return !(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_QR);
+}
+
+/* Read a label from a DNS packet, escape "\" and ".", append \0 */
+static gchar *escape_label(guint8* src, guint src_length, gchar **ret_name, guint *ret_name_length) {
+ gchar *r;
+
+ g_assert(src);
+ g_assert(ret_name);
+ g_assert(*ret_name);
+ g_assert(ret_name_length);
+ g_assert(*ret_name_length > 0);
+
+ r = *ret_name;
+
+ while (src_length > 0) {
+ if (*src == '.' || *src == '\\') {
+ if (*ret_name_length < 3)
+ return NULL;
+
+ *((*ret_name) ++) = '\\';
+ (*ret_name_length) --;
+ }
+
+ if (*ret_name_length < 2)
+ return NULL;
+
+ *((*ret_name)++) = *src;
+ (*ret_name_length) --;
+
+ src_length --;
+ src++;
+ }
+
+ **ret_name = 0;
+
+ return r;
+}
+
+static gint consume_labels(AvahiDnsPacket *p, guint index, gchar *ret_name, guint l) {
+ gint ret = 0;
+ int compressed = 0;
+ int first_label = 1;
+ g_assert(p && ret_name && l);
+
+ for (;;) {
+ guint8 n;
+
+ if (index+1 > p->size)
+ return -1;
+
+ n = AVAHI_DNS_PACKET_DATA(p)[index];
+
+ if (!n) {
+ index++;
+ if (!compressed)
+ ret++;
+
+ if (l < 1)
+ return -1;
+ *ret_name = 0;
+
+ return ret;
+
+ } else if (n <= 63) {
+ /* Uncompressed label */
+ index++;
+ if (!compressed)
+ ret++;
+
+ if (index + n > p->size)
+ return -1;
+
+ if ((guint) n + 1 > l)
+ return -1;
+
+ if (!first_label) {
+ *(ret_name++) = '.';
+ l--;
+ } else
+ first_label = 0;
+
+ if (!(escape_label(AVAHI_DNS_PACKET_DATA(p) + index, n, &ret_name, &l)))
+ return -1;
+
+ index += n;
+
+ if (!compressed)
+ ret += n;
+ } else if ((n & 0xC0) == 0xC0) {
+ /* Compressed label */
+
+ if (index+2 > p->size)
+ return -1;
+
+ index = ((guint) (AVAHI_DNS_PACKET_DATA(p)[index] & ~0xC0)) << 8 | AVAHI_DNS_PACKET_DATA(p)[index+1];
+
+ if (!compressed)
+ ret += 2;
+
+ compressed = 1;
+ } else
+ return -1;
+ }
+}
+
+gint avahi_dns_packet_consume_name(AvahiDnsPacket *p, gchar *ret_name, guint l) {
+ gint r;
+
+ if ((r = consume_labels(p, p->rindex, ret_name, l)) < 0)
+ return -1;
+
+ p->rindex += r;
+ return 0;
+}
+
+gint avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, guint16 *ret_v) {
+ g_assert(p);
+ g_assert(ret_v);
+
+ if (p->rindex + sizeof(guint16) > p->size)
+ return -1;
+
+ *ret_v = g_ntohs(*((guint16*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex)));
+ p->rindex += sizeof(guint16);
+
+ return 0;
+}
+
+gint avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, guint32 *ret_v) {
+ g_assert(p);
+ g_assert(ret_v);
+
+ if (p->rindex + sizeof(guint32) > p->size)
+ return -1;
+
+ *ret_v = g_ntohl(*((guint32*) (AVAHI_DNS_PACKET_DATA(p) + p->rindex)));
+ p->rindex += sizeof(guint32);
+
+ return 0;
+}
+
+gint avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, gpointer ret_data, guint l) {
+ g_assert(p);
+ g_assert(ret_data);
+ g_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;
+}
+
+gint avahi_dns_packet_consume_string(AvahiDnsPacket *p, gchar *ret_string, guint l) {
+ guint k;
+
+ g_assert(p);
+ g_assert(ret_string);
+ g_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;
+
+}
+
+gconstpointer avahi_dns_packet_get_rptr(AvahiDnsPacket *p) {
+ g_assert(p);
+
+ if (p->rindex > p->size)
+ return NULL;
+
+ return AVAHI_DNS_PACKET_DATA(p) + p->rindex;
+}
+
+gint avahi_dns_packet_skip(AvahiDnsPacket *p, guint length) {
+ g_assert(p);
+
+ if (p->rindex + length > p->size)
+ return -1;
+
+ p->rindex += length;
+ return 0;
+}
+
+AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, gboolean *ret_cache_flush) {
+ gchar name[257], buf[257];
+ guint16 type, class;
+ guint32 ttl;
+ guint16 rdlength;
+ AvahiRecord *r = NULL;
+ gconstpointer start;
+
+ g_assert(p);
+ g_assert(ret_cache_flush);
+
+/* g_message("consume_record()"); */
+
+ 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;
+
+/* g_message("name = %s, rdlength = %u", name, rdlength); */
+
+ *ret_cache_flush = !!(class & AVAHI_DNS_CACHE_FLUSH);
+ class &= ~AVAHI_DNS_CACHE_FLUSH;
+
+ start = avahi_dns_packet_get_rptr(p);
+
+ r = avahi_record_new_full(name, class, type);
+
+ switch (type) {
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+
+/* g_message("ptr"); */
+
+ if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
+ goto fail;
+
+ r->data.ptr.name = g_strdup(buf);
+ break;
+
+
+ case AVAHI_DNS_TYPE_SRV:
+
+/* g_message("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)
+ goto fail;
+
+ r->data.srv.name = g_strdup(buf);
+ break;
+
+ case AVAHI_DNS_TYPE_HINFO:
+
+/* g_message("hinfo"); */
+
+ if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
+ goto fail;
+
+ r->data.hinfo.cpu = g_strdup(buf);
+
+ if (avahi_dns_packet_consume_string(p, buf, sizeof(buf)) < 0)
+ goto fail;
+
+ r->data.hinfo.os = g_strdup(buf);
+ break;
+
+ case AVAHI_DNS_TYPE_TXT:
+
+/* g_message("txt"); */
+
+ if (rdlength > 0) {
+ r->data.txt.string_list = avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength);
+
+ if (avahi_dns_packet_skip(p, rdlength) < 0)
+ goto fail;
+ } else
+ r->data.txt.string_list = NULL;
+
+ break;
+
+ case AVAHI_DNS_TYPE_A:
+
+/* g_message("A"); */
+
+ if (avahi_dns_packet_consume_bytes(p, &r->data.a.address, sizeof(AvahiIPv4Address)) < 0)
+ goto fail;
+
+ break;
+
+ case AVAHI_DNS_TYPE_AAAA:
+
+/* g_message("aaaa"); */
+
+ if (avahi_dns_packet_consume_bytes(p, &r->data.aaaa.address, sizeof(AvahiIPv6Address)) < 0)
+ goto fail;
+
+ break;
+
+ default:
+
+/* g_message("generic"); */
+
+ if (rdlength > 0) {
+
+ r->data.generic.data = g_memdup(avahi_dns_packet_get_rptr(p), rdlength);
+
+ if (avahi_dns_packet_skip(p, rdlength) < 0)
+ goto fail;
+ }
+
+ break;
+ }
+
+/* g_message("%i == %u ?", (guint8*) avahi_dns_packet_get_rptr(p) - (guint8*) start, rdlength); */
+
+ /* Check if we read enough data */
+ if ((guint8*) avahi_dns_packet_get_rptr(p) - (guint8*) start != rdlength)
+ goto fail;
+
+ r->ttl = ttl;
+
+ return r;
+
+fail:
+ if (r)
+ avahi_record_unref(r);
+
+ return NULL;
+}
+
+AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, gboolean *ret_unicast_response) {
+ gchar name[256];
+ guint16 type, class;
+
+ g_assert(p);
+ g_assert(ret_unicast_response);
+
+ if (avahi_dns_packet_consume_name(p, name, sizeof(name)) < 0 ||
+ avahi_dns_packet_consume_uint16(p, &type) < 0 ||
+ avahi_dns_packet_consume_uint16(p, &class) < 0)
+ return NULL;
+
+ *ret_unicast_response = !!(class & AVAHI_DNS_UNICAST_RESPONSE);
+ class &= ~AVAHI_DNS_UNICAST_RESPONSE;
+
+ return avahi_key_new(name, class, type);
+}
+
+guint8* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, gboolean unicast_response) {
+ guint8 *t;
+ guint size;
+
+ g_assert(p);
+ g_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->class | (unicast_response ? AVAHI_DNS_UNICAST_RESPONSE : 0))) {
+ p->size = size;
+ return NULL;
+ }
+
+ return t;
+}
+
+guint8* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, gboolean cache_flush) {
+ guint8 *t, *l, *start;
+ guint size;
+
+ g_assert(p);
+ g_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->class | AVAHI_DNS_CACHE_FLUSH) : (r->key->class &~ AVAHI_DNS_CACHE_FLUSH)) ||
+ !avahi_dns_packet_append_uint32(p, r->ttl) ||
+ !(l = avahi_dns_packet_append_uint16(p, 0)))
+ goto fail;
+
+ start = avahi_dns_packet_extend(p, 0);
+
+ switch (r->key->type) {
+
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME :
+
+ if (!(avahi_dns_packet_append_name(p, r->data.ptr.name)))
+ goto fail;
+
+ 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))
+ goto fail;
+
+ 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))
+ goto fail;
+
+ break;
+
+ case AVAHI_DNS_TYPE_TXT: {
+
+ guint8 *data;
+ guint size;
+
+ size = avahi_string_list_serialize(r->data.txt.string_list, NULL, 0);
+
+/* g_message("appending string: %u %p", size, r->data.txt.string_list); */
+
+ if (!(data = avahi_dns_packet_extend(p, size)))
+ goto fail;
+
+ avahi_string_list_serialize(r->data.txt.string_list, data, size);
+ break;
+ }
+
+
+ case AVAHI_DNS_TYPE_A:
+
+ if (!avahi_dns_packet_append_bytes(p, &r->data.a.address, sizeof(r->data.a.address)))
+ goto fail;
+
+ break;
+
+ case AVAHI_DNS_TYPE_AAAA:
+
+ if (!avahi_dns_packet_append_bytes(p, &r->data.aaaa.address, sizeof(r->data.aaaa.address)))
+ goto fail;
+
+ break;
+
+ default:
+
+ if (r->data.generic.size &&
+ avahi_dns_packet_append_bytes(p, r->data.generic.data, r->data.generic.size))
+ goto fail;
+
+ break;
+ }
+
+
+
+
+ size = avahi_dns_packet_extend(p, 0) - start;
+ g_assert(size <= 0xFFFF);
+
+/* g_message("appended %u", size); */
+
+ * (guint16*) l = g_htons((guint16) size);
+
+ return t;
+
+
+fail:
+ p->size = size;
+ return NULL;
+}
+
+gboolean avahi_dns_packet_is_empty(AvahiDnsPacket *p) {
+ g_assert(p);
+
+ return p->size <= AVAHI_DNS_PACKET_HEADER_SIZE;
+}
+
+guint avahi_dns_packet_space(AvahiDnsPacket *p) {
+ g_assert(p);
+
+ g_assert(p->size <= p->max_size);
+
+ return p->max_size - p->size;
+}
diff --git a/avahi-core/dns.h b/avahi-core/dns.h
new file mode 100644
index 0000000..61a9cb6
--- /dev/null
+++ b/avahi-core/dns.h
@@ -0,0 +1,103 @@
+#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 <glib.h>
+
+#include "rr.h"
+
+#define AVAHI_DNS_PACKET_MAX_SIZE 9000
+#define AVAHI_DNS_PACKET_HEADER_SIZE 12
+
+typedef struct _AvahiDnsPacket {
+ guint size, rindex, max_size;
+ GHashTable *name_table; /* for name compression */
+} AvahiDnsPacket;
+
+
+#define AVAHI_DNS_PACKET_DATA(p) (((guint8*) p) + sizeof(AvahiDnsPacket))
+
+AvahiDnsPacket* avahi_dns_packet_new(guint size);
+AvahiDnsPacket* avahi_dns_packet_new_query(guint size);
+AvahiDnsPacket* avahi_dns_packet_new_response(guint size);
+
+void avahi_dns_packet_free(AvahiDnsPacket *p);
+void avahi_dns_packet_set_field(AvahiDnsPacket *p, guint index, guint16 v);
+guint16 avahi_dns_packet_get_field(AvahiDnsPacket *p, guint index);
+
+guint8 *avahi_dns_packet_extend(AvahiDnsPacket *p, guint l);
+
+guint8 *avahi_dns_packet_append_uint16(AvahiDnsPacket *p, guint16 v);
+guint8 *avahi_dns_packet_append_uint32(AvahiDnsPacket *p, guint32 v);
+guint8 *avahi_dns_packet_append_name(AvahiDnsPacket *p, const gchar *name);
+guint8 *avahi_dns_packet_append_bytes(AvahiDnsPacket *p, gconstpointer, guint l);
+guint8* avahi_dns_packet_append_key(AvahiDnsPacket *p, AvahiKey *k, gboolean unicast_response);
+guint8* avahi_dns_packet_append_record(AvahiDnsPacket *p, AvahiRecord *r, gboolean cache_flush);
+guint8* avahi_dns_packet_append_string(AvahiDnsPacket *p, const gchar *s);
+
+gint avahi_dns_packet_is_query(AvahiDnsPacket *p);
+gint avahi_dns_packet_check_valid(AvahiDnsPacket *p);
+
+gint avahi_dns_packet_consume_uint16(AvahiDnsPacket *p, guint16 *ret_v);
+gint avahi_dns_packet_consume_uint32(AvahiDnsPacket *p, guint32 *ret_v);
+gint avahi_dns_packet_consume_name(AvahiDnsPacket *p, gchar *ret_name, guint l);
+gint avahi_dns_packet_consume_bytes(AvahiDnsPacket *p, gpointer ret_data, guint l);
+AvahiKey* avahi_dns_packet_consume_key(AvahiDnsPacket *p, gboolean *ret_unicast_response);
+AvahiRecord* avahi_dns_packet_consume_record(AvahiDnsPacket *p, gboolean *ret_cache_flush);
+gint avahi_dns_packet_consume_string(AvahiDnsPacket *p, gchar *ret_string, guint l);
+
+gconstpointer avahi_dns_packet_get_rptr(AvahiDnsPacket *p);
+
+gint avahi_dns_packet_skip(AvahiDnsPacket *p, guint length);
+
+gboolean avahi_dns_packet_is_empty(AvahiDnsPacket *p);
+guint 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_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \
+ (((guint16) !!qr << 15) | \
+ ((guint16) (opcode & 15) << 11) | \
+ ((guint16) !!aa << 10) | \
+ ((guint16) !!tc << 9) | \
+ ((guint16) !!rd << 8) | \
+ ((guint16) !!ra << 7) | \
+ ((guint16) !!ad << 5) | \
+ ((guint16) !!cd << 4) | \
+ ((guint16) (rd & 15)))
+
+
+gchar *avahi_unescape_label(gchar *dest, guint size, const gchar **name);
+
+#endif
+
diff --git a/avahi-core/domain-test.c b/avahi-core/domain-test.c
new file mode 100644
index 0000000..7f6bd7b
--- /dev/null
+++ b/avahi-core/domain-test.c
@@ -0,0 +1,44 @@
+/* $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 "util.h"
+
+int main(int argc, char *argv[]) {
+ gchar *s;
+
+ g_message("host name: %s", s = avahi_get_host_name());
+ g_free(s);
+
+ g_message("%s", s = avahi_normalize_name("foo.foo."));
+ g_free(s);
+
+ g_message("%s", s = avahi_normalize_name("foo.foo."));
+ g_free(s);
+
+
+ g_message("%i", avahi_domain_equal("\\aaa bbb\\.cccc\\\\.dee.fff.", "aaa\\ bbb\\.cccc\\\\.dee.fff"));
+
+ return 0;
+}
diff --git a/avahi-core/iface.c b/avahi-core/iface.c
new file mode 100644
index 0000000..13ffcef
--- /dev/null
+++ b/avahi-core/iface.c
@@ -0,0 +1,586 @@
+/* $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 <sys/socket.h>
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <errno.h>
+#include <net/if.h>
+
+#include "iface.h"
+#include "netlink.h"
+#include "dns.h"
+#include "socket.h"
+#include "announce.h"
+
+static void update_address_rr(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a, int remove) {
+ g_assert(m);
+ g_assert(a);
+
+ if (!avahi_interface_address_relevant(a) || remove) {
+ if (a->entry_group) {
+ avahi_entry_group_free(a->entry_group);
+ a->entry_group = NULL;
+ }
+ } else {
+ if (!a->entry_group) {
+ a->entry_group = avahi_entry_group_new(m->server, NULL, NULL);
+ avahi_server_add_address(m->server, a->entry_group, a->interface->hardware->index, AF_UNSPEC, 0, NULL, &a->address);
+ avahi_entry_group_commit(a->entry_group);
+ }
+ }
+}
+
+static void update_interface_rr(AvahiInterfaceMonitor *m, AvahiInterface *i, int remove) {
+ AvahiInterfaceAddress *a;
+ g_assert(m);
+ g_assert(i);
+
+ for (a = i->addresses; a; a = a->address_next)
+ update_address_rr(m, a, remove);
+}
+
+static void update_hw_interface_rr(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, int remove) {
+ AvahiInterface *i;
+
+ g_assert(m);
+ g_assert(hw);
+
+ for (i = hw->interfaces; i; i = i->by_hardware_next)
+ update_interface_rr(m, i, remove);
+}
+
+static void free_address(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a) {
+ g_assert(m);
+ g_assert(a);
+ g_assert(a->interface);
+
+ AVAHI_LLIST_REMOVE(AvahiInterfaceAddress, address, a->interface->addresses, a);
+
+ if (a->entry_group)
+ avahi_entry_group_free(a->entry_group);
+
+ g_free(a);
+}
+
+static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, gboolean send_goodbye) {
+ g_assert(m);
+ g_assert(i);
+
+ g_message("removing interface %s.%i", i->hardware->name, i->protocol);
+ avahi_goodbye_interface(m->server, i, send_goodbye);
+ g_message("flushing...");
+ avahi_packet_scheduler_flush_responses(i->scheduler);
+ g_message("done");
+
+ g_assert(!i->announcements);
+
+ while (i->addresses)
+ free_address(m, i->addresses);
+
+ avahi_packet_scheduler_free(i->scheduler);
+ avahi_cache_free(i->cache);
+
+ AVAHI_LLIST_REMOVE(AvahiInterface, interface, m->interfaces, i);
+ AVAHI_LLIST_REMOVE(AvahiInterface, by_hardware, i->hardware->interfaces, i);
+
+ g_free(i);
+}
+
+static void free_hw_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, gboolean send_goodbye) {
+ g_assert(m);
+ g_assert(hw);
+
+ while (hw->interfaces)
+ free_interface(m, hw->interfaces, send_goodbye);
+
+ AVAHI_LLIST_REMOVE(AvahiHwInterface, hardware, m->hw_interfaces, hw);
+ g_hash_table_remove(m->hash_table, &hw->index);
+
+ g_free(hw->name);
+ g_free(hw);
+}
+
+static AvahiInterfaceAddress* get_address(AvahiInterfaceMonitor *m, AvahiInterface *i, const AvahiAddress *raddr) {
+ AvahiInterfaceAddress *ia;
+
+ g_assert(m);
+ g_assert(i);
+ g_assert(raddr);
+
+ for (ia = i->addresses; ia; ia = ia->address_next)
+ if (avahi_address_cmp(&ia->address, raddr) == 0)
+ return ia;
+
+ return NULL;
+}
+
+static int netlink_list_items(AvahiNetlink *nl, guint16 type, guint *ret_seq) {
+ struct nlmsghdr *n;
+ struct rtgenmsg *gen;
+ guint8 req[1024];
+
+ 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_MATCH|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 new_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, guchar protocol) {
+ AvahiInterface *i;
+
+ g_assert(m);
+ g_assert(hw);
+ g_assert(protocol != AF_UNSPEC);
+
+ i = g_new(AvahiInterface, 1);
+ i->monitor = m;
+ i->hardware = hw;
+ i->protocol = protocol;
+ i->announcing = FALSE;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiInterfaceAddress, i->addresses);
+ AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, i->announcements);
+
+ i->cache = avahi_cache_new(m->server, i);
+ i->scheduler = avahi_packet_scheduler_new(m->server, i);
+
+ AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i);
+ AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i);
+}
+
+static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *i) {
+ gboolean b;
+
+ g_assert(m);
+ g_assert(i);
+
+ b = avahi_interface_relevant(i);
+
+ if (b && !i->announcing) {
+ g_message("New relevant interface %s.%i", i->hardware->name, i->protocol);
+
+ if (i->protocol == AF_INET)
+ avahi_mdns_mcast_join_ipv4 (i->hardware->index, m->server->fd_ipv4);
+ if (i->protocol == AF_INET6)
+ avahi_mdns_mcast_join_ipv6 (i->hardware->index, m->server->fd_ipv6);
+
+ i->announcing = TRUE;
+ avahi_announce_interface(m->server, i);
+ } else if (!b && i->announcing) {
+ g_message("Interface %s.%i no longer relevant", i->hardware->name, i->protocol);
+
+ avahi_goodbye_interface(m->server, i, FALSE);
+
+ if (i->protocol == AF_INET)
+ avahi_mdns_mcast_leave_ipv4 (i->hardware->index, m->server->fd_ipv4);
+ if (i->protocol == AF_INET6)
+ avahi_mdns_mcast_leave_ipv6 (i->hardware->index, m->server->fd_ipv6);
+
+ i->announcing = FALSE;
+ }
+}
+
+static void check_hw_interface_relevant(AvahiInterfaceMonitor *m, AvahiHwInterface *hw) {
+ AvahiInterface *i;
+
+ g_assert(m);
+ g_assert(hw);
+
+ for (i = hw->interfaces; i; i = i->by_hardware_next)
+ check_interface_relevant(m, i);
+}
+
+static void callback(AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata) {
+ AvahiInterfaceMonitor *m = userdata;
+
+ g_assert(m);
+ g_assert(n);
+ g_assert(m->netlink == nl);
+
+ if (n->nlmsg_type == RTM_NEWLINK) {
+ struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
+ AvahiHwInterface *hw;
+ struct rtattr *a = NULL;
+ size_t l;
+
+ if (ifinfomsg->ifi_family != AF_UNSPEC)
+ return;
+
+ if (!(hw = g_hash_table_lookup(m->hash_table, &ifinfomsg->ifi_index))) {
+ hw = g_new(AvahiHwInterface, 1);
+ hw->monitor = m;
+ hw->name = NULL;
+ hw->flags = 0;
+ hw->mtu = 1500;
+ hw->index = ifinfomsg->ifi_index;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiInterface, hw->interfaces);
+ AVAHI_LLIST_PREPEND(AvahiHwInterface, hardware, m->hw_interfaces, hw);
+
+ g_hash_table_insert(m->hash_table, &hw->index, hw);
+
+ if (m->server->fd_ipv4 >= 0)
+ new_interface(m, hw, AF_INET);
+ if (m->server->fd_ipv6 >= 0)
+ new_interface(m, hw, AF_INET6);
+ }
+
+ hw->flags = ifinfomsg->ifi_flags;
+
+ l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
+ a = IFLA_RTA(ifinfomsg);
+
+ while (RTA_OK(a, l)) {
+ switch(a->rta_type) {
+ case IFLA_IFNAME:
+ g_free(hw->name);
+ hw->name = g_strndup(RTA_DATA(a), RTA_PAYLOAD(a));
+ break;
+
+ case IFLA_MTU:
+ g_assert(RTA_PAYLOAD(a) == sizeof(unsigned int));
+ hw->mtu = *((unsigned int*) RTA_DATA(a));
+ break;
+
+ default:
+ ;
+ }
+
+ a = RTA_NEXT(a, l);
+ }
+
+ update_hw_interface_rr(m, hw, FALSE);
+ check_hw_interface_relevant(m, hw);
+
+ } else if (n->nlmsg_type == RTM_DELLINK) {
+ struct ifinfomsg *ifinfomsg = NLMSG_DATA(n);
+ AvahiHwInterface *hw;
+
+ if (ifinfomsg->ifi_family != AF_UNSPEC)
+ return;
+
+ if (!(hw = avahi_interface_monitor_get_hw_interface(m, ifinfomsg->ifi_index)))
+ return;
+
+ update_hw_interface_rr(m, hw, TRUE);
+ free_hw_interface(m, hw, FALSE);
+
+ } else if (n->nlmsg_type == RTM_NEWADDR || n->nlmsg_type == RTM_DELADDR) {
+
+ struct ifaddrmsg *ifaddrmsg = NLMSG_DATA(n);
+ AvahiInterface *i;
+ struct rtattr *a = NULL;
+ size_t l;
+ AvahiAddress raddr;
+ int raddr_valid = 0;
+
+ if (ifaddrmsg->ifa_family != AF_INET && ifaddrmsg->ifa_family != AF_INET6)
+ return;
+
+ if (!(i = (AvahiInterface*) avahi_interface_monitor_get_interface(m, ifaddrmsg->ifa_index, ifaddrmsg->ifa_family)))
+ return;
+
+ raddr.family = ifaddrmsg->ifa_family;
+
+ l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg));
+ a = IFA_RTA(ifaddrmsg);
+
+ while (RTA_OK(a, l)) {
+ switch(a->rta_type) {
+ case IFA_ADDRESS:
+ if ((raddr.family == AF_INET6 && RTA_PAYLOAD(a) != 16) ||
+ (raddr.family == AF_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 (!raddr_valid)
+ return;
+
+ if (n->nlmsg_type == RTM_NEWADDR) {
+ AvahiInterfaceAddress *addr;
+
+ if (!(addr = get_address(m, i, &raddr))) {
+ addr = g_new(AvahiInterfaceAddress, 1);
+ addr->monitor = m;
+ addr->address = raddr;
+ addr->interface = i;
+ addr->entry_group = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiInterfaceAddress, address, i->addresses, addr);
+ }
+
+ addr->flags = ifaddrmsg->ifa_flags;
+ addr->scope = ifaddrmsg->ifa_scope;
+
+ update_address_rr(m, addr, FALSE);
+ check_interface_relevant(m, i);
+ } else {
+ AvahiInterfaceAddress *addr;
+
+ if (!(addr = get_address(m, i, &raddr)))
+ return;
+
+ update_address_rr(m, addr, TRUE);
+ free_address(m, addr);
+
+ check_interface_relevant(m, i);
+ }
+
+ } else if (n->nlmsg_type == NLMSG_DONE) {
+
+ if (m->list == LIST_IFACE) {
+ m->list = LIST_DONE;
+
+ if (netlink_list_items(m->netlink, RTM_GETADDR, &m->query_addr_seq) < 0)
+ g_warning("NETLINK: Failed to list addrs: %s", strerror(errno));
+ else
+ m->list = LIST_ADDR;
+ } else {
+ m->list = LIST_DONE;
+ g_message("Enumeration complete");
+ }
+
+ } else if (n->nlmsg_type == NLMSG_ERROR && (n->nlmsg_seq == m->query_link_seq || n->nlmsg_seq == m->query_addr_seq)) {
+ struct nlmsgerr *e = NLMSG_DATA (n);
+
+ if (e->error)
+ g_warning("NETLINK: Failed to browse: %s", strerror(-e->error));
+ }
+}
+
+AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *s) {
+ AvahiInterfaceMonitor *m = NULL;
+
+ m = g_new0(AvahiInterfaceMonitor, 1);
+ m->server = s;
+ if (!(m->netlink = avahi_netlink_new(s->context, G_PRIORITY_DEFAULT-10, RTMGRP_LINK|RTMGRP_IPV4_IFADDR|RTMGRP_IPV6_IFADDR, callback, m)))
+ goto fail;
+
+ m->hash_table = g_hash_table_new(g_int_hash, g_int_equal);
+
+ AVAHI_LLIST_HEAD_INIT(AvahiInterface, m->interfaces);
+ AVAHI_LLIST_HEAD_INIT(AvahiHwInterface, m->hw_interfaces);
+
+ if (netlink_list_items(m->netlink, RTM_GETLINK, &m->query_link_seq) < 0)
+ goto fail;
+
+ m->list = LIST_IFACE;
+
+ return m;
+
+fail:
+ avahi_interface_monitor_free(m);
+ return NULL;
+}
+
+void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m) {
+ g_assert(m);
+
+ while (m->list != LIST_DONE) {
+ if (!avahi_netlink_work(m->netlink, TRUE))
+ break;
+ }
+}
+
+void avahi_interface_monitor_free(AvahiInterfaceMonitor *m) {
+ g_assert(m);
+
+ while (m->hw_interfaces)
+ free_hw_interface(m, m->hw_interfaces, TRUE);
+
+ g_assert(!m->interfaces);
+
+
+ if (m->netlink)
+ avahi_netlink_free(m->netlink);
+
+ if (m->hash_table)
+ g_hash_table_destroy(m->hash_table);
+
+ g_free(m);
+}
+
+
+AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, gint index, guchar protocol) {
+ AvahiHwInterface *hw;
+ AvahiInterface *i;
+
+ g_assert(m);
+ g_assert(index > 0);
+ g_assert(protocol != AF_UNSPEC);
+
+ if (!(hw = avahi_interface_monitor_get_hw_interface(m, index)))
+ 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, gint index) {
+ g_assert(m);
+ g_assert(index > 0);
+
+ return g_hash_table_lookup(m->hash_table, &index);
+}
+
+
+void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) {
+ g_assert(i);
+ g_assert(p);
+
+ if (avahi_interface_relevant(i)) {
+ g_message("sending on '%s.%i'", i->hardware->name, i->protocol);
+
+ if (i->protocol == AF_INET && i->monitor->server->fd_ipv4 >= 0)
+ avahi_send_dns_packet_ipv4(i->monitor->server->fd_ipv4, i->hardware->index, p);
+ else if (i->protocol == AF_INET6 && i->monitor->server->fd_ipv6 >= 0)
+ avahi_send_dns_packet_ipv6(i->monitor->server->fd_ipv6, i->hardware->index, p);
+ }
+}
+
+void avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, gboolean immediately) {
+ g_assert(i);
+ g_assert(key);
+
+ if (avahi_interface_relevant(i))
+ avahi_packet_scheduler_post_query(i->scheduler, key, immediately);
+}
+
+
+void avahi_interface_post_response(AvahiInterface *i, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately) {
+ g_assert(i);
+ g_assert(record);
+
+ if (avahi_interface_relevant(i))
+ avahi_packet_scheduler_post_response(i->scheduler, a, record, flush_cache, immediately);
+}
+
+void avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, gboolean immediately) {
+ g_assert(i);
+ g_assert(record);
+
+ if (avahi_interface_relevant(i))
+ avahi_packet_scheduler_post_probe(i->scheduler, record, immediately);
+}
+
+void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f) {
+ AvahiInterface *i;
+ g_assert(m);
+
+ for (i = m->interfaces; i; i = i->interface_next) {
+ if (avahi_interface_relevant(i)) {
+ fprintf(f, "\n;;; INTERFACE %s.%i ;;;\n", i->hardware->name, i->protocol);
+ avahi_cache_dump(i->cache, f);
+ }
+ }
+ fprintf(f, "\n");
+}
+
+gboolean avahi_interface_relevant(AvahiInterface *i) {
+ g_assert(i);
+
+ return
+ (i->hardware->flags & IFF_UP) &&
+ (i->hardware->flags & IFF_RUNNING) &&
+ !(i->hardware->flags & IFF_LOOPBACK) &&
+ (i->hardware->flags & IFF_MULTICAST) &&
+ i->addresses;
+}
+
+gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a) {
+ g_assert(a);
+
+ return a->scope == RT_SCOPE_UNIVERSE;
+}
+
+
+gboolean avahi_interface_match(AvahiInterface *i, gint index, guchar protocol) {
+ g_assert(i);
+
+ if (index > 0 && index != i->hardware->index)
+ return FALSE;
+
+ if (protocol != AF_UNSPEC && protocol != i->protocol)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, gint interface, guchar protocol, AvahiInterfaceMonitorWalkCallback callback, gpointer userdata) {
+ g_assert(m);
+ g_assert(callback);
+
+ if (interface > 0) {
+ if (protocol != AF_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);
+ }
+}
diff --git a/avahi-core/iface.h b/avahi-core/iface.h
new file mode 100644
index 0000000..74136aa
--- /dev/null
+++ b/avahi-core/iface.h
@@ -0,0 +1,123 @@
+#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.
+***/
+
+#include <glib.h>
+
+typedef struct _AvahiInterfaceMonitor AvahiInterfaceMonitor;
+typedef struct _AvahiInterfaceAddress AvahiInterfaceAddress;
+typedef struct _AvahiInterface AvahiInterface;
+typedef struct _AvahiHwInterface AvahiHwInterface;
+
+#include "address.h"
+#include "server.h"
+#include "netlink.h"
+#include "cache.h"
+#include "llist.h"
+#include "psched.h"
+#include "dns.h"
+#include "announce.h"
+
+struct _AvahiInterfaceMonitor {
+ AvahiServer *server;
+ AvahiNetlink *netlink;
+ GHashTable *hash_table;
+
+ AVAHI_LLIST_HEAD(AvahiInterface, interfaces);
+ AVAHI_LLIST_HEAD(AvahiHwInterface, hw_interfaces);
+
+ guint query_addr_seq, query_link_seq;
+
+ enum {
+ LIST_IFACE,
+ LIST_ADDR,
+ LIST_DONE
+ } list;
+};
+
+struct _AvahiHwInterface {
+ AVAHI_LLIST_FIELDS(AvahiHwInterface, hardware);
+ AvahiInterfaceMonitor *monitor;
+
+ gchar *name;
+ gint index;
+ guint flags;
+ guint mtu;
+
+ AVAHI_LLIST_HEAD(AvahiInterface, interfaces);
+};
+
+struct _AvahiInterface {
+ AVAHI_LLIST_FIELDS(AvahiInterface, interface);
+ AVAHI_LLIST_FIELDS(AvahiInterface, by_hardware);
+ AvahiInterfaceMonitor *monitor;
+
+ AvahiHwInterface *hardware;
+ guchar protocol;
+ gboolean announcing;
+
+ AvahiCache *cache;
+ AvahiPacketScheduler *scheduler;
+
+ AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses);
+ AVAHI_LLIST_HEAD(AvahiAnnouncement, announcements);
+};
+
+struct _AvahiInterfaceAddress {
+ AVAHI_LLIST_FIELDS(AvahiInterfaceAddress, address);
+ AvahiInterfaceMonitor *monitor;
+
+ guchar flags;
+ guchar scope;
+ AvahiAddress address;
+
+ AvahiEntryGroup *entry_group;
+ AvahiInterface *interface;
+};
+
+AvahiInterfaceMonitor *avahi_interface_monitor_new(AvahiServer *server);
+void avahi_interface_monitor_free(AvahiInterfaceMonitor *m);
+
+void avahi_interface_monitor_sync(AvahiInterfaceMonitor *m);
+
+AvahiInterface* avahi_interface_monitor_get_interface(AvahiInterfaceMonitor *m, gint index, guchar protocol);
+AvahiHwInterface* avahi_interface_monitor_get_hw_interface(AvahiInterfaceMonitor *m, gint index);
+
+void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p);
+
+void avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, gboolean immediately);
+void avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, gboolean immediately);
+void avahi_interface_post_response(AvahiInterface *i, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately);
+
+void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f);
+
+gboolean avahi_interface_relevant(AvahiInterface *i);
+gboolean avahi_interface_address_relevant(AvahiInterfaceAddress *a);
+
+gboolean avahi_interface_match(AvahiInterface *i, gint index, guchar protocol);
+
+typedef void (*AvahiInterfaceMonitorWalkCallback)(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata);
+
+void avahi_interface_monitor_walk(AvahiInterfaceMonitor *m, gint index, guchar protocol, AvahiInterfaceMonitorWalkCallback callback, gpointer userdata);
+
+#endif
diff --git a/avahi-core/llist.h b/avahi-core/llist.h
new file mode 100644
index 0000000..7c28f31
--- /dev/null
+++ b/avahi-core/llist.h
@@ -0,0 +1,71 @@
+#ifndef foollistfoo
+#define foollistfoo
+
+/* $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 <glib.h>
+
+/* Some macros for maintaining doubly linked lists */
+
+/* The head of the linked list. Use this in the structure that shall
+ * contain the head of the linked list */
+#define AVAHI_LLIST_HEAD(t,name) t *name
+
+/* The pointers in the linked list's items. Use this in the item structure */
+#define AVAHI_LLIST_FIELDS(t,name) t *name##_next, *name##_prev
+
+/* Initialize the list's head */
+#define AVAHI_LLIST_HEAD_INIT(t,head) do { (head) = NULL; } while(0)
+
+/* Initialize a list item */
+#define AVAHI_LLIST_INIT(t,name,item) do { \
+ t *_item = (item); \
+ g_assert(_item); \
+ _item->name##_prev = _item->name##_next = NULL; \
+ } while(0)
+
+/* Prepend an item to the list */
+#define AVAHI_LLIST_PREPEND(t,name,head,item) do { \
+ t **_head = &(head), *_item = (item); \
+ g_assert(_item); \
+ if ((_item->name##_next = *_head)) \
+ _item->name##_next->name##_prev = _item; \
+ _item->name##_prev = NULL; \
+ *_head = _item; \
+ } while (0)
+
+/* Remove an item from the list */
+#define AVAHI_LLIST_REMOVE(t,name,head,item) do { \
+ t **_head = &(head), *_item = (item); \
+ g_assert(_item); \
+ if (_item->name##_next) \
+ _item->name##_next->name##_prev = _item->name##_prev; \
+ if (_item->name##_prev) \
+ _item->name##_prev->name##_next = _item->name##_next; \
+ else {\
+ g_assert(*_head == _item); \
+ *_head = _item->name##_next; \
+ } \
+ _item->name##_next = _item->name##_prev = NULL; \
+ } while(0)
+
+#endif
diff --git a/avahi-core/netlink.c b/avahi-core/netlink.c
new file mode 100644
index 0000000..92ed436
--- /dev/null
+++ b/avahi-core/netlink.c
@@ -0,0 +1,187 @@
+/* $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 "netlink.h"
+
+struct _AvahiNetlink {
+ GMainContext *context;
+ gint fd;
+ guint seq;
+ GPollFD poll_fd;
+ GSource *source;
+ void (*callback) (AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata);
+ gpointer userdata;
+};
+
+gboolean avahi_netlink_work(AvahiNetlink *nl, gboolean block) {
+ g_assert(nl);
+
+ for (;;) {
+ guint8 replybuf[64*1024];
+ ssize_t bytes;
+ struct nlmsghdr *p = (struct nlmsghdr *) replybuf;
+
+ if ((bytes = recv(nl->fd, replybuf, sizeof(replybuf), block ? 0 : MSG_DONTWAIT)) < 0) {
+
+ if (errno == EAGAIN || errno == EINTR)
+ break;
+
+ g_warning("NETLINK: recv() failed");
+ return FALSE;
+ }
+
+ if (nl->callback) {
+ for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
+ if (!NLMSG_OK(p, (size_t) bytes)) {
+ g_warning("NETLINK: packet truncated");
+ return FALSE;
+ }
+
+ nl->callback(nl, p, nl->userdata);
+ }
+ }
+
+ if (block)
+ break;
+ }
+
+ return TRUE;
+}
+
+static gboolean prepare_func(GSource *source, gint *timeout) {
+ g_assert(source);
+ g_assert(timeout);
+
+ *timeout = -1;
+ return FALSE;
+}
+
+static gboolean check_func(GSource *source) {
+ AvahiNetlink* nl;
+ g_assert(source);
+
+ nl = *((AvahiNetlink**) (((guint8*) source) + sizeof(GSource)));
+ g_assert(nl);
+
+ return nl->poll_fd.revents & (G_IO_IN|G_IO_HUP|G_IO_ERR);
+}
+
+static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
+ AvahiNetlink* nl;
+ g_assert(source);
+
+ nl = *((AvahiNetlink**) (((guint8*) source) + sizeof(GSource)));
+ g_assert(nl);
+
+ return avahi_netlink_work(nl, FALSE);
+}
+
+AvahiNetlink *avahi_netlink_new(GMainContext *context, gint priority, guint32 groups, void (*cb) (AvahiNetlink *nl, struct nlmsghdr *n, gpointer userdata), gpointer userdata) {
+ int fd;
+ struct sockaddr_nl addr;
+ AvahiNetlink *nl;
+
+ static GSourceFuncs source_funcs = {
+ prepare_func,
+ check_func,
+ dispatch_func,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ g_assert(context);
+ g_assert(cb);
+
+ if ((fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
+ g_critical("NETLINK: 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) {
+ close(fd);
+ g_critical("bind(): %s", strerror(errno));
+ return NULL;
+ }
+
+ nl = g_new(AvahiNetlink, 1);
+ nl->context = context;
+ g_main_context_ref(context);
+ nl->fd = fd;
+ nl->seq = 0;
+ nl->callback = cb;
+ nl->userdata = userdata;
+
+ nl->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiNetlink*));
+ *((AvahiNetlink**) (((guint8*) nl->source) + sizeof(GSource))) = nl;
+
+ g_source_set_priority(nl->source, priority);
+
+ memset(&nl->poll_fd, 0, sizeof(GPollFD));
+ nl->poll_fd.fd = fd;
+ nl->poll_fd.events = G_IO_IN|G_IO_ERR|G_IO_HUP;
+ g_source_add_poll(nl->source, &nl->poll_fd);
+
+ g_source_attach(nl->source, nl->context);
+
+ return nl;
+}
+
+void avahi_netlink_free(AvahiNetlink *nl) {
+ g_assert(nl);
+
+ g_source_destroy(nl->source);
+ g_source_unref(nl->source);
+ g_main_context_unref(nl->context);
+ close(nl->fd);
+ g_free(nl);
+}
+
+int avahi_netlink_send(AvahiNetlink *nl, struct nlmsghdr *m, guint *ret_seq) {
+ g_assert(nl);
+ g_assert(m);
+
+ m->nlmsg_seq = nl->seq++;
+ m->nlmsg_flags |= NLM_F_ACK;
+
+ if (send(nl->fd, m, m->nlmsg_len, 0) < 0) {
+ g_warning("NETLINK: send(): %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (ret_seq)
+ *ret_seq = m->nlmsg_seq;
+
+ return 0;
+}
diff --git a/avahi-core/netlink.h b/avahi-core/netlink.h
new file mode 100644
index 0000000..f6be54a
--- /dev/null
+++ b/avahi-core/netlink.h
@@ -0,0 +1,41 @@
+#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 <linux/netlink.h>
+
+#include <glib.h>
+
+struct _AvahiNetlink;
+typedef struct _AvahiNetlink AvahiNetlink;
+
+AvahiNetlink *avahi_netlink_new(GMainContext *c, gint priority, guint32 groups, void (*cb) (AvahiNetlink *n, struct nlmsghdr *m, gpointer userdata), gpointer userdata);
+void avahi_netlink_free(AvahiNetlink *n);
+
+int avahi_netlink_send(AvahiNetlink *n, struct nlmsghdr *m, guint *ret_seq);
+
+gboolean avahi_netlink_work(AvahiNetlink *n, gboolean block);
+
+#endif
diff --git a/avahi-core/prioq-test.c b/avahi-core/prioq-test.c
new file mode 100644
index 0000000..28c734b
--- /dev/null
+++ b/avahi-core/prioq-test.c
@@ -0,0 +1,116 @@
+/* $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 "prioq.h"
+
+static gint compare_int(gconstpointer a, gconstpointer b) {
+ gint i = GPOINTER_TO_INT(a), j = GPOINTER_TO_INT(b);
+
+ return i < j ? -1 : (i > j ? 1 : 0);
+}
+
+static int compare_ptr(gconstpointer a, gconstpointer b) {
+ return a < b ? -1 : (a > b ? 1 : 0);
+}
+
+static void rec(AvahiPrioQueueNode *n) {
+ if (!n)
+ return;
+
+ if (n->left)
+ g_assert(n->left->parent == n);
+
+ if (n->right)
+ g_assert(n->right->parent == n);
+
+ if (n->parent) {
+ g_assert(n->parent->left == n || n->parent->right == n);
+
+ if (n->parent->left == n)
+ g_assert(n->next == n->parent->right);
+ }
+
+ if (!n->next) {
+ g_assert(n->queue->last == n);
+
+ if (n->parent && n->parent->left == n)
+ g_assert(n->parent->right == NULL);
+ }
+
+
+ if (n->parent) {
+ int a = GPOINTER_TO_INT(n->parent->data), b = GPOINTER_TO_INT(n->data);
+ if (a > b) {
+ printf("%i <= %i: NO\n", a, b);
+ abort();
+ }
+ }
+
+ rec(n->left);
+ rec(n->right);
+}
+
+int main(int argc, char *argv[]) {
+ AvahiPrioQueue *q, *q2;
+ gint 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, GINT_TO_POINTER(random() & 0xFFFF)));
+
+ while (q2->root) {
+ rec(q->root);
+ rec(q2->root);
+
+ g_assert(q->n_nodes == q2->n_nodes);
+
+ printf("%i\n", GPOINTER_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) { */
+/* gint v = GPOINTER_TO_INT(q->root->data); */
+/* rec(q->root); */
+/* printf("%i\n", v); */
+/* avahi_prio_queue_remove(q, q->root); */
+/* g_assert(v >= prev); */
+/* prev = v; */
+/* } */
+
+ avahi_prio_queue_free(q);
+ return 0;
+}
diff --git a/avahi-core/prioq.c b/avahi-core/prioq.c
new file mode 100644
index 0000000..7e57ae5
--- /dev/null
+++ b/avahi-core/prioq.c
@@ -0,0 +1,384 @@
+/* $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 "prioq.h"
+
+AvahiPrioQueue* avahi_prio_queue_new(gint (*compare) (gconstpointer a, gconstpointer b)) {
+ AvahiPrioQueue *q;
+ g_assert(compare);
+
+ q = g_new(AvahiPrioQueue, 1);
+ q->root = q->last = NULL;
+ q->n_nodes = 0;
+ q->compare = compare;
+ return q;
+}
+
+void avahi_prio_queue_free(AvahiPrioQueue *q) {
+ g_assert(q);
+
+ while (q->last)
+ avahi_prio_queue_remove(q, q->last);
+
+ g_assert(!q->n_nodes);
+ g_free(q);
+}
+
+static AvahiPrioQueueNode* get_node_at_xy(AvahiPrioQueue *q, guint x, guint y) {
+ guint r;
+ AvahiPrioQueueNode *n;
+ g_assert(q);
+
+ n = q->root;
+ g_assert(n);
+
+ for (r = 0; r < y; r++) {
+ g_assert(n);
+
+ if ((x >> (y-r-1)) & 1)
+ n = n->right;
+ else
+ n = n->left;
+ }
+
+ g_assert(n->x == x);
+ g_assert(n->y == y);
+
+ return n;
+}
+
+static void exchange_nodes(AvahiPrioQueue *q, AvahiPrioQueueNode *a, AvahiPrioQueueNode *b) {
+ AvahiPrioQueueNode *l, *r, *p, *ap, *an, *bp, *bn;
+ gint t;
+ g_assert(q);
+ g_assert(a);
+ g_assert(b);
+ g_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) {
+ g_assert(q);
+ g_assert(n);
+
+ /* 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 */
+ g_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, gpointer data) {
+ AvahiPrioQueueNode *n;
+ g_assert(q);
+
+ n = g_new(AvahiPrioQueueNode, 1);
+ n->queue = q;
+ n->data = data;
+
+ if (q->last) {
+ g_assert(q->root);
+ g_assert(q->n_nodes);
+
+ n->y = q->last->y;
+ n->x = q->last->x+1;
+
+ if (n->x >= ((guint) 1 << n->y)) {
+ n->x = 0;
+ n->y++;
+ }
+
+ q->last->next = n;
+ n->prev = q->last;
+
+ g_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 {
+ g_assert(!q->root);
+ g_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) {
+ g_assert(q);
+ g_assert(n);
+
+ 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;
+ }
+
+ g_assert(n == q->last);
+ g_assert(!n->next);
+ g_assert(!n->left);
+ g_assert(!n->right);
+
+ q->last = n->prev;
+
+ if (n->prev) {
+ n->prev->next = NULL;
+ g_assert(n->parent);
+ } else
+ g_assert(!n->parent);
+
+ if (n->parent) {
+ g_assert(n->prev);
+ if (n->parent->left == n) {
+ if (n->parent->right != NULL) {
+ g_message("fuck");
+ for (;;);
+
+ }
+
+ g_assert(n->parent->right == NULL);
+ n->parent->left = NULL;
+ } else {
+ g_assert(n->parent->right == n);
+ g_assert(n->parent->left != NULL);
+ n->parent->right = NULL;
+ }
+ } else {
+ g_assert(q->root == n);
+ g_assert(!n->prev);
+ g_assert(q->n_nodes == 1);
+ q->root = NULL;
+ }
+
+ g_free(n);
+
+ g_assert(q->n_nodes > 0);
+ q->n_nodes--;
+}
+
diff --git a/avahi-core/prioq.h b/avahi-core/prioq.h
new file mode 100644
index 0000000..6651071
--- /dev/null
+++ b/avahi-core/prioq.h
@@ -0,0 +1,56 @@
+#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.
+***/
+
+#include <glib.h>
+
+struct _AvahiPrioQueue;
+typedef struct _AvahiPrioQueue AvahiPrioQueue;
+
+struct _AvahiPrioQueueNode;
+typedef struct _AvahiPrioQueueNode AvahiPrioQueueNode;
+
+struct _AvahiPrioQueue {
+ AvahiPrioQueueNode *root, *last;
+
+ guint n_nodes;
+ gint (*compare) (gconstpointer a, gconstpointer b);
+};
+
+struct _AvahiPrioQueueNode {
+ AvahiPrioQueue *queue;
+ gpointer data;
+ guint x, y;
+
+ AvahiPrioQueueNode *left, *right, *parent, *next, *prev;
+};
+
+AvahiPrioQueue* avahi_prio_queue_new(gint (*compare) (gconstpointer a, gconstpointer b));
+void avahi_prio_queue_free(AvahiPrioQueue *q);
+
+AvahiPrioQueueNode* avahi_prio_queue_put(AvahiPrioQueue *q, gpointer data);
+void avahi_prio_queue_remove(AvahiPrioQueue *q, AvahiPrioQueueNode *n);
+
+void avahi_prio_queue_shuffle(AvahiPrioQueue *q, AvahiPrioQueueNode *n);
+
+#endif
diff --git a/avahi-core/psched.c b/avahi-core/psched.c
new file mode 100644
index 0000000..249e279
--- /dev/null
+++ b/avahi-core/psched.c
@@ -0,0 +1,737 @@
+/* $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 "util.h"
+#include "psched.h"
+
+#define AVAHI_QUERY_HISTORY_MSEC 100
+#define AVAHI_QUERY_DEFER_MSEC 100
+#define AVAHI_RESPONSE_HISTORY_MSEC 700
+#define AVAHI_RESPONSE_DEFER_MSEC 20
+#define AVAHI_RESPONSE_JITTER_MSEC 100
+#define AVAHI_PROBE_DEFER_MSEC 70
+
+AvahiPacketScheduler *avahi_packet_scheduler_new(AvahiServer *server, AvahiInterface *i) {
+ AvahiPacketScheduler *s;
+
+ g_assert(server);
+ g_assert(i);
+
+ s = g_new(AvahiPacketScheduler, 1);
+ s->server = server;
+ s->interface = i;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->query_jobs);
+ AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->response_jobs);
+ AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers);
+ AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->probe_jobs);
+
+ return s;
+}
+
+static void query_job_free(AvahiPacketScheduler *s, AvahiQueryJob *qj) {
+ g_assert(qj);
+
+ if (qj->time_event)
+ avahi_time_event_queue_remove(qj->scheduler->server->time_event_queue, qj->time_event);
+
+ AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->query_jobs, qj);
+
+ avahi_key_unref(qj->key);
+ g_free(qj);
+}
+
+static void response_job_free(AvahiPacketScheduler *s, AvahiResponseJob *rj) {
+ g_assert(rj);
+
+ if (rj->time_event)
+ avahi_time_event_queue_remove(rj->scheduler->server->time_event_queue, rj->time_event);
+
+ AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->response_jobs, rj);
+
+ avahi_record_unref(rj->record);
+ g_free(rj);
+}
+
+static void probe_job_free(AvahiPacketScheduler *s, AvahiProbeJob *pj) {
+ g_assert(pj);
+
+ if (pj->time_event)
+ avahi_time_event_queue_remove(pj->scheduler->server->time_event_queue, pj->time_event);
+
+ AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->probe_jobs, pj);
+
+ avahi_record_unref(pj->record);
+ g_free(pj);
+}
+
+void avahi_packet_scheduler_free(AvahiPacketScheduler *s) {
+ AvahiQueryJob *qj;
+ AvahiResponseJob *rj;
+ AvahiProbeJob *pj;
+
+ g_assert(s);
+
+ g_assert(!s->known_answers);
+
+ while ((qj = s->query_jobs))
+ query_job_free(s, qj);
+ while ((rj = s->response_jobs))
+ response_job_free(s, rj);
+ while ((pj = s->probe_jobs))
+ probe_job_free(s, pj);
+
+ g_free(s);
+}
+
+static gpointer known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
+ AvahiPacketScheduler *s = userdata;
+ AvahiKnownAnswer *ka;
+
+ g_assert(c);
+ g_assert(pattern);
+ g_assert(e);
+ g_assert(s);
+
+ if (avahi_cache_entry_half_ttl(c, e))
+ return NULL;
+
+ ka = g_new0(AvahiKnownAnswer, 1);
+ ka->scheduler = s;
+ ka->record = avahi_record_ref(e->record);
+
+ AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka);
+ return NULL;
+}
+
+static guint8* packet_add_query_job(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) {
+ guint8 *d;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(qj);
+
+ if ((d = avahi_dns_packet_append_key(p, qj->key, FALSE))) {
+ GTimeVal tv;
+
+ qj->done = 1;
+
+ /* Drop query after some time from history */
+ avahi_elapse_time(&tv, AVAHI_QUERY_HISTORY_MSEC, 0);
+ avahi_time_event_queue_update(s->server->time_event_queue, qj->time_event, &tv);
+
+ g_get_current_time(&qj->delivery);
+
+ /* Add all matching known answers to the list */
+ avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s);
+ }
+
+ return d;
+}
+
+static void append_known_answers_and_send(AvahiPacketScheduler *s, AvahiDnsPacket *p) {
+ AvahiKnownAnswer *ka;
+ guint n;
+ g_assert(s);
+ g_assert(p);
+
+ n = 0;
+
+ while ((ka = s->known_answers)) {
+
+ while (!avahi_dns_packet_append_record(p, ka->record, FALSE)) {
+
+ g_assert(!avahi_dns_packet_is_empty(p));
+
+ 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 - 48);
+ n = 0;
+ }
+
+ AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka);
+ avahi_record_unref(ka->record);
+ g_free(ka);
+
+ 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 query_elapse(AvahiTimeEvent *e, gpointer data) {
+ AvahiQueryJob *qj = data;
+ AvahiPacketScheduler *s;
+ AvahiDnsPacket *p;
+ guint n;
+ guint8 *d;
+
+ g_assert(qj);
+ s = qj->scheduler;
+
+ if (qj->done) {
+ /* Lets remove it from the history */
+ query_job_free(s, qj);
+ return;
+ }
+
+ g_assert(!s->known_answers);
+
+ p = avahi_dns_packet_new_query(s->interface->hardware->mtu - 48);
+ d = packet_add_query_job(s, p, qj);
+ g_assert(d);
+ n = 1;
+
+ /* Try to fill up packet with more queries, if available */
+ for (qj = s->query_jobs; qj; qj = qj->jobs_next) {
+
+ if (qj->done)
+ continue;
+
+ if (!packet_add_query_job(s, p, qj))
+ break;
+
+ n++;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
+
+ /* Now add known answers */
+ append_known_answers_and_send(s, p);
+}
+
+AvahiQueryJob* query_job_new(AvahiPacketScheduler *s, AvahiKey *key) {
+ AvahiQueryJob *qj;
+
+ g_assert(s);
+ g_assert(key);
+
+ qj = g_new(AvahiQueryJob, 1);
+ qj->scheduler = s;
+ qj->key = avahi_key_ref(key);
+ qj->done = FALSE;
+ qj->time_event = NULL;
+
+ AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->query_jobs, qj);
+
+ return qj;
+}
+
+void avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *key, gboolean immediately) {
+ GTimeVal tv;
+ AvahiQueryJob *qj;
+
+ g_assert(s);
+ g_assert(key);
+
+ avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0);
+
+ for (qj = s->query_jobs; qj; qj = qj->jobs_next) {
+
+ if (avahi_key_equal(qj->key, key)) {
+
+ glong d = avahi_timeval_diff(&tv, &qj->delivery);
+
+ /* Duplicate questions suppression */
+ if (d >= 0 && d <= AVAHI_QUERY_HISTORY_MSEC*1000) {
+ g_message("WARNING! DUPLICATE QUERY SUPPRESSION ACTIVE!");
+ return;
+ }
+
+ query_job_free(s, qj);
+ break;
+ }
+
+ }
+
+ qj = query_job_new(s, key);
+ qj->delivery = tv;
+ qj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &qj->delivery, query_elapse, qj);
+}
+
+static guint8* packet_add_response_job(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) {
+ guint8 *d;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(rj);
+
+ if ((d = avahi_dns_packet_append_record(p, rj->record, rj->flush_cache))) {
+ GTimeVal tv;
+
+ rj->done = 1;
+
+ /* Drop response after some time from history */
+ avahi_elapse_time(&tv, AVAHI_RESPONSE_HISTORY_MSEC, 0);
+ avahi_time_event_queue_update(s->server->time_event_queue, rj->time_event, &tv);
+
+ g_get_current_time(&rj->delivery);
+ }
+
+ return d;
+}
+
+static void send_response_packet(AvahiPacketScheduler *s, AvahiResponseJob *rj) {
+ AvahiDnsPacket *p;
+ guint n;
+
+ g_assert(s);
+
+ p = avahi_dns_packet_new_response(s->interface->hardware->mtu - 200);
+ n = 0;
+
+ /* If a job was specified, put it in the packet. */
+ if (rj) {
+ guint8 *d;
+ d = packet_add_response_job(s, p, rj);
+ g_assert(d);
+ n++;
+ }
+
+ /* Try to fill up packet with more responses, if available */
+ for (rj = s->response_jobs; rj; rj = rj->jobs_next) {
+
+ if (rj->done)
+ continue;
+
+ if (!packet_add_response_job(s, p, rj))
+ break;
+
+ 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 response_elapse(AvahiTimeEvent *e, gpointer data) {
+ AvahiResponseJob *rj = data;
+ AvahiPacketScheduler *s;
+
+ g_assert(rj);
+ s = rj->scheduler;
+
+ if (rj->done) {
+ /* Lets remove it from the history */
+ response_job_free(s, rj);
+ return;
+ }
+
+ send_response_packet(s, rj);
+}
+
+static AvahiResponseJob* look_for_response(AvahiPacketScheduler *s, AvahiRecord *record) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+ g_assert(record);
+
+ for (rj = s->response_jobs; rj; rj = rj->jobs_next)
+ if (avahi_record_equal_no_ttl(rj->record, record))
+ return rj;
+
+ return NULL;
+}
+
+static AvahiResponseJob* response_job_new(AvahiPacketScheduler *s, AvahiRecord *record) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+ g_assert(record);
+
+ rj = g_new(AvahiResponseJob, 1);
+ rj->scheduler = s;
+ rj->record = avahi_record_ref(record);
+ rj->done = FALSE;
+ rj->time_event = NULL;
+ rj->address_valid = FALSE;
+ rj->flush_cache = FALSE;
+
+ AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->response_jobs, rj);
+
+ return rj;
+}
+
+void avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately) {
+ AvahiResponseJob *rj;
+ GTimeVal tv;
+
+ g_assert(s);
+ g_assert(record);
+
+ g_assert(!avahi_key_is_pattern(record->key));
+
+ avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC);
+
+ /* Don't send out duplicates */
+
+ if ((rj = look_for_response(s, record))) {
+ glong d;
+
+ d = avahi_timeval_diff(&tv, &rj->delivery);
+
+ /* If there's already a matching packet in our history or in
+ * the schedule, we do nothing. */
+ if (!!record->ttl == !!rj->record->ttl &&
+ d >= 0 && d <= AVAHI_RESPONSE_HISTORY_MSEC*1000) {
+ g_message("WARNING! DUPLICATE RESPONSE SUPPRESSION ACTIVE!");
+
+ /* This job is no longer specific to a single querier, so
+ * make sure it isn't suppressed by known answer
+ * suppresion */
+
+ if (rj->address_valid && (!a || avahi_address_cmp(a, &rj->address) != 0))
+ rj->address_valid = FALSE;
+
+ rj->flush_cache = flush_cache;
+
+ return;
+ }
+
+ /* Either one was a goodbye packet, but the other was not, so
+ * let's drop the older one. */
+ response_job_free(s, rj);
+ }
+
+/* g_message("ACCEPTED NEW RESPONSE [%s]", t = avahi_record_to_string(record)); */
+/* g_free(t); */
+
+ /* Create a new job and schedule it */
+ rj = response_job_new(s, record);
+ rj->flush_cache = flush_cache;
+ rj->delivery = tv;
+ rj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &rj->delivery, response_elapse, rj);
+
+ /* Store the address of the host this messages is intended to, so
+ that we can drop this job in case a truncated message with
+ known answer suppresion entries is recieved */
+
+ if ((rj->address_valid = !!a))
+ rj->address = *a;
+}
+
+void avahi_packet_scheduler_incoming_query(AvahiPacketScheduler *s, AvahiKey *key) {
+ GTimeVal tv;
+ AvahiQueryJob *qj;
+
+ g_assert(s);
+ g_assert(key);
+
+ /* This function is called whenever an incoming query was
+ * receieved. We drop all scheduled queries which match here. The
+ * keyword is "DUPLICATE QUESTION SUPPRESION". */
+
+ for (qj = s->query_jobs; qj; qj = qj->jobs_next)
+ if (avahi_key_equal(qj->key, key)) {
+
+ if (qj->done)
+ return;
+
+ goto mark_done;
+ }
+
+
+ /* No matching job was found. Add the query to the history */
+ qj = query_job_new(s, key);
+
+mark_done:
+ qj->done = TRUE;
+
+ /* Drop the query after some time */
+ avahi_elapse_time(&tv, AVAHI_QUERY_HISTORY_MSEC, 0);
+ qj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &tv, query_elapse, qj);
+
+ g_get_current_time(&qj->delivery);
+}
+
+void response_job_set_elapse_time(AvahiPacketScheduler *s, AvahiResponseJob *rj, guint msec, guint jitter) {
+ GTimeVal tv;
+
+ g_assert(s);
+ g_assert(rj);
+
+ avahi_elapse_time(&tv, msec, jitter);
+
+ if (rj->time_event)
+ avahi_time_event_queue_update(s->server->time_event_queue, rj->time_event, &tv);
+ else
+ rj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &tv, response_elapse, rj);
+
+}
+
+void avahi_packet_scheduler_incoming_response(AvahiPacketScheduler *s, AvahiRecord *record) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+ g_assert(record);
+
+ /* This function is called whenever an incoming response was
+ * receieved. We drop all scheduled responses which match
+ * here. The keyword is "DUPLICATE ANSWER SUPPRESION". */
+
+ for (rj = s->response_jobs; rj; rj = rj->jobs_next)
+ if (avahi_record_equal_no_ttl(rj->record, record)) {
+
+ if (rj->done) {
+
+ if (!!record->ttl == !!rj->record->ttl) {
+ /* An entry like this is already in our history,
+ * so let's get out of here! */
+
+ return;
+
+ } else {
+ /* Either one was a goodbye packet but other was
+ * none. We remove the history entry, and add a
+ * new one */
+
+ response_job_free(s, rj);
+ break;
+ }
+
+ } else {
+
+ if (!!record->ttl == !!rj->record->ttl) {
+
+ /* The incoming packet matches our scheduled
+ * record, so let's mark that one as done */
+
+ goto mark_done;
+
+ } else {
+
+ /* Either one was a goodbye packet but other was
+ * none. We ignore the incoming packet. */
+
+ return;
+ }
+ }
+ }
+
+ /* No matching job was found. Add the query to the history */
+ rj = response_job_new(s, record);
+
+mark_done:
+ rj->done = TRUE;
+
+ /* Drop response after 500ms from history */
+ response_job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0);
+
+ g_get_current_time(&rj->delivery);
+}
+
+void avahi_packet_scheduler_incoming_known_answer(AvahiPacketScheduler *s, AvahiRecord *record, const AvahiAddress *a) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+ g_assert(record);
+ g_assert(a);
+
+ for (rj = s->response_jobs; rj; rj = rj->jobs_next) {
+
+ g_assert(record->ttl > 0);
+ g_assert(rj->record->ttl/2);
+
+ if (avahi_record_equal_no_ttl(rj->record, record))
+ if (rj->address_valid)
+ if (avahi_address_cmp(&rj->address, a))
+ if (record->ttl >= rj->record->ttl/2) {
+
+ /* Let's suppress it */
+
+ response_job_free(s, rj);
+ break;
+ }
+ }
+}
+
+void avahi_packet_scheduler_flush_responses(AvahiPacketScheduler *s) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+
+ /* Send all scheduled responses, ignoring the scheduled time */
+
+ for (rj = s->response_jobs; rj; rj = rj->jobs_next)
+ if (!rj->done)
+ send_response_packet(s, rj);
+}
+
+static AvahiProbeJob* probe_job_new(AvahiPacketScheduler *s, AvahiRecord *record) {
+ AvahiProbeJob *pj;
+
+ g_assert(s);
+ g_assert(record);
+
+ pj = g_new(AvahiProbeJob, 1);
+ pj->scheduler = s;
+ pj->record = avahi_record_ref(record);
+ pj->time_event = NULL;
+ pj->chosen = FALSE;
+
+ AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->probe_jobs, pj);
+
+ return pj;
+}
+
+static guint8* packet_add_probe_query(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) {
+ guint size;
+ guint8 *ret;
+ AvahiKey *k;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(pj);
+
+ g_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 NULL;
+
+ /* Create the probe query */
+ k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY);
+ ret = avahi_dns_packet_append_key(p, k, FALSE);
+ g_assert(ret);
+
+ /* Mark this job for addition to the packet */
+ pj->chosen = TRUE;
+
+ /* Scan for more jobs whith matching key pattern */
+ for (pj = s->probe_jobs; pj; pj = pj->jobs_next) {
+ if (pj->chosen)
+ continue;
+
+ /* Does the record match the probe? */
+ if (k->class != pj->record->key->class || !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 = TRUE;
+ }
+
+ avahi_key_unref(k);
+
+ return ret;
+}
+
+static void probe_elapse(AvahiTimeEvent *e, gpointer data) {
+ AvahiProbeJob *pj = data, *next;
+ AvahiPacketScheduler *s;
+ AvahiDnsPacket *p;
+ guint n;
+
+ g_assert(pj);
+ s = pj->scheduler;
+
+ p = avahi_dns_packet_new_query(s->interface->hardware->mtu - 48);
+
+ /* Add the import probe */
+ if (!packet_add_probe_query(s, p, pj)) {
+ g_warning("Record too large! ---");
+ avahi_dns_packet_free(p);
+ return;
+ }
+
+ n = 1;
+
+ /* Try to fill up packet with more probes, if available */
+ for (pj = s->probe_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->probe_jobs; pj; pj = next) {
+
+ next = pj->jobs_next;
+
+ if (!pj->chosen)
+ continue;
+
+ if (!avahi_dns_packet_append_record(p, pj->record, TRUE)) {
+ g_warning("Bad probe size estimate!");
+
+ /* Unmark all following jobs */
+ for (; pj; pj = pj->jobs_next)
+ pj->chosen = FALSE;
+
+ break;
+ }
+
+ probe_job_free(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);
+}
+
+void avahi_packet_scheduler_post_probe(AvahiPacketScheduler *s, AvahiRecord *record, gboolean immediately) {
+ AvahiProbeJob *pj;
+ GTimeVal tv;
+
+ g_assert(s);
+ g_assert(record);
+ g_assert(!avahi_key_is_pattern(record->key));
+
+ avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0);
+
+ /* Create a new job and schedule it */
+ pj = probe_job_new(s, record);
+ pj->delivery = tv;
+ pj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &pj->delivery, probe_elapse, pj);
+}
diff --git a/avahi-core/psched.h b/avahi-core/psched.h
new file mode 100644
index 0000000..72f8ef0
--- /dev/null
+++ b/avahi-core/psched.h
@@ -0,0 +1,99 @@
+#ifndef foopschedhfoo
+#define foopschedhfoo
+
+/* $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 _AvahiQueryJob AvahiQueryJob;
+typedef struct _AvahiResponseJob AvahiResponseJob;
+typedef struct _AvahiPacketScheduler AvahiPacketScheduler;
+typedef struct _AvahiKnownAnswer AvahiKnownAnswer;
+typedef struct _AvahiProbeJob AvahiProbeJob;
+
+#include "timeeventq.h"
+#include "rr.h"
+#include "llist.h"
+#include "iface.h"
+
+struct _AvahiQueryJob {
+ AvahiPacketScheduler *scheduler;
+ AvahiTimeEvent *time_event;
+ AvahiKey *key;
+ gboolean done;
+ GTimeVal delivery;
+ AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs);
+};
+
+struct _AvahiResponseJob {
+ AvahiPacketScheduler *scheduler;
+ AvahiTimeEvent *time_event;
+ AvahiRecord *record;
+ AvahiAddress address;
+ gboolean address_valid;
+ gboolean done;
+ GTimeVal delivery;
+ gboolean flush_cache;
+ AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs);
+};
+
+struct _AvahiKnownAnswer {
+ AvahiPacketScheduler *scheduler;
+ AvahiRecord *record;
+
+ AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer);
+};
+
+struct _AvahiProbeJob {
+ AvahiPacketScheduler *scheduler;
+ AvahiTimeEvent *time_event;
+ AvahiRecord *record;
+
+ gboolean chosen; /* Use for packet assembling */
+ GTimeVal delivery;
+
+ AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs);
+};
+
+struct _AvahiPacketScheduler {
+ AvahiServer *server;
+
+ AvahiInterface *interface;
+
+ AVAHI_LLIST_HEAD(AvahiQueryJob, query_jobs);
+ AVAHI_LLIST_HEAD(AvahiResponseJob, response_jobs);
+ AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers);
+ AVAHI_LLIST_HEAD(AvahiProbeJob, probe_jobs);
+};
+
+AvahiPacketScheduler *avahi_packet_scheduler_new(AvahiServer *server, AvahiInterface *i);
+void avahi_packet_scheduler_free(AvahiPacketScheduler *s);
+
+void avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *key, gboolean immediately);
+void avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, const AvahiAddress *a, AvahiRecord *record, gboolean flush_cache, gboolean immediately);
+void avahi_packet_scheduler_post_probe(AvahiPacketScheduler *s, AvahiRecord *record, gboolean immediately);
+
+void avahi_packet_scheduler_incoming_query(AvahiPacketScheduler *s, AvahiKey *key);
+void avahi_packet_scheduler_incoming_response(AvahiPacketScheduler *s, AvahiRecord *record);
+void avahi_packet_scheduler_incoming_known_answer(AvahiPacketScheduler *s, AvahiRecord *record, const AvahiAddress *a);
+
+void avahi_packet_scheduler_flush_responses(AvahiPacketScheduler *s);
+
+#endif
diff --git a/avahi-core/rr.c b/avahi-core/rr.c
new file mode 100644
index 0000000..cbc39a5
--- /dev/null
+++ b/avahi-core/rr.c
@@ -0,0 +1,581 @@
+/* $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 "util.h"
+#include "rr.h"
+#include "dns.h"
+
+AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type) {
+ AvahiKey *k;
+ g_assert(name);
+
+ k = g_new(AvahiKey, 1);
+ k->ref = 1;
+ k->name = avahi_normalize_name(name);
+ k->class = class;
+ k->type = type;
+
+/* g_message("%p %% ref=1", k); */
+
+ return k;
+}
+
+AvahiKey *avahi_key_ref(AvahiKey *k) {
+ g_assert(k);
+ g_assert(k->ref >= 1);
+
+ k->ref++;
+
+/* g_message("%p ++ ref=%i", k, k->ref); */
+
+ return k;
+}
+
+void avahi_key_unref(AvahiKey *k) {
+ g_assert(k);
+ g_assert(k->ref >= 1);
+
+/* g_message("%p -- ref=%i", k, k->ref-1); */
+
+ if ((--k->ref) <= 0) {
+ g_free(k->name);
+ g_free(k);
+ }
+}
+
+AvahiRecord *avahi_record_new(AvahiKey *k) {
+ AvahiRecord *r;
+
+ g_assert(k);
+
+ r = g_new(AvahiRecord, 1);
+ r->ref = 1;
+ r->key = avahi_key_ref(k);
+
+ memset(&r->data, 0, sizeof(r->data));
+
+ r->ttl = AVAHI_DEFAULT_TTL;
+
+ return r;
+}
+
+AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type) {
+ AvahiRecord *r;
+ AvahiKey *k;
+
+ g_assert(name);
+
+ k = avahi_key_new(name, class, type);
+ r = avahi_record_new(k);
+ avahi_key_unref(k);
+
+ return r;
+}
+
+AvahiRecord *avahi_record_ref(AvahiRecord *r) {
+ g_assert(r);
+ g_assert(r->ref >= 1);
+
+ r->ref++;
+ return r;
+}
+
+void avahi_record_unref(AvahiRecord *r) {
+ g_assert(r);
+ g_assert(r->ref >= 1);
+
+ if ((--r->ref) <= 0) {
+ switch (r->key->type) {
+
+ case AVAHI_DNS_TYPE_SRV:
+ g_free(r->data.srv.name);
+ break;
+
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ g_free(r->data.ptr.name);
+ break;
+
+ case AVAHI_DNS_TYPE_HINFO:
+ g_free(r->data.hinfo.cpu);
+ g_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:
+ g_free(r->data.generic.data);
+ }
+
+ avahi_key_unref(r->key);
+ g_free(r);
+ }
+}
+
+const gchar *avahi_dns_class_to_string(guint16 class) {
+ if (class & AVAHI_DNS_CACHE_FLUSH)
+ return "FLUSH";
+
+ if (class == AVAHI_DNS_CLASS_IN)
+ return "IN";
+
+ return NULL;
+}
+
+const gchar *avahi_dns_type_to_string(guint16 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";
+ default:
+ return NULL;
+ }
+}
+
+
+gchar *avahi_key_to_string(const AvahiKey *k) {
+ return g_strdup_printf("%s\t%s\t%s",
+ k->name,
+ avahi_dns_class_to_string(k->class),
+ avahi_dns_type_to_string(k->type));
+}
+
+gchar *avahi_record_to_string(const AvahiRecord *r) {
+ gchar *p, *s;
+ char buf[257], *t = NULL, *d = NULL;
+
+ 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 :
+
+ 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;
+ }
+
+ p = avahi_key_to_string(r->key);
+ s = g_strdup_printf("%s %s ; ttl=%u", p, t ? t : "<unparsable>", r->ttl);
+ g_free(p);
+ g_free(d);
+
+ return s;
+}
+
+gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b) {
+ g_assert(a);
+ g_assert(b);
+
+ if (a == b)
+ return TRUE;
+
+/* g_message("equal: %p %p", a, b); */
+
+ return avahi_domain_equal(a->name, b->name) &&
+ a->type == b->type &&
+ a->class == b->class;
+}
+
+gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k) {
+ g_assert(pattern);
+ g_assert(k);
+
+/* g_message("equal: %p %p", a, b); */
+
+ g_assert(!avahi_key_is_pattern(k));
+
+ if (pattern == k)
+ return TRUE;
+
+ return avahi_domain_equal(pattern->name, k->name) &&
+ (pattern->type == k->type || pattern->type == AVAHI_DNS_TYPE_ANY) &&
+ pattern->class == k->class;
+}
+
+gboolean avahi_key_is_pattern(const AvahiKey *k) {
+ g_assert(k);
+
+ return k->type == AVAHI_DNS_TYPE_ANY;
+}
+
+
+guint avahi_key_hash(const AvahiKey *k) {
+ g_assert(k);
+
+ return avahi_domain_hash(k->name) + k->type + k->class;
+}
+
+static gboolean rdata_equal(const AvahiRecord *a, const AvahiRecord *b) {
+ g_assert(a);
+ g_assert(b);
+ g_assert(a->key->type == b->key->type);
+
+/* t = avahi_record_to_string(a); */
+/* g_message("comparing %s", t); */
+/* g_free(t); */
+
+/* t = avahi_record_to_string(b); */
+/* g_message("and %s", t); */
+/* g_free(t); */
+
+
+ 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:
+ 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);
+ }
+
+}
+
+gboolean avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b) {
+ g_assert(a);
+ g_assert(b);
+
+ if (a == b)
+ return TRUE;
+
+ return
+ avahi_key_equal(a->key, b->key) &&
+ rdata_equal(a, b);
+}
+
+
+AvahiRecord *avahi_record_copy(AvahiRecord *r) {
+ AvahiRecord *copy;
+
+ copy = g_new(AvahiRecord, 1);
+ 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:
+ copy->data.ptr.name = g_strdup(r->data.ptr.name);
+ 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;
+ copy->data.srv.name = g_strdup(r->data.srv.name);
+ break;
+
+ case AVAHI_DNS_TYPE_HINFO:
+ copy->data.hinfo.os = g_strdup(r->data.hinfo.os);
+ copy->data.hinfo.cpu = g_strdup(r->data.hinfo.cpu);
+ 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:
+ copy->data.generic.data = g_memdup(r->data.generic.data, r->data.generic.size);
+ copy->data.generic.size = r->data.generic.size;
+ break;
+
+ }
+
+ return copy;
+}
+
+
+guint avahi_key_get_estimate_size(AvahiKey *k) {
+ g_assert(k);
+
+ return strlen(k->name)+1+4;
+}
+
+guint avahi_record_get_estimate_size(AvahiRecord *r) {
+ guint n;
+ g_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:
+ 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 gint lexicographical_memcmp(gconstpointer a, size_t al, gconstpointer b, size_t bl) {
+ size_t c;
+ gint ret;
+
+ g_assert(a);
+ g_assert(b);
+
+ c = al < bl ? al : bl;
+ if ((ret = memcmp(a, b, c)) != 0)
+ return ret;
+
+ if (al == bl)
+ return 0;
+ else
+ return al == c ? 1 : -1;
+}
+
+static gint uint16_cmp(guint16 a, guint16 b) {
+ return a == b ? 0 : (a < b ? a : b);
+}
+
+static gint lexicographical_domain_cmp(const gchar *a, const gchar *b) {
+ g_assert(a);
+ g_assert(b);
+
+
+ for (;;) {
+ gchar t1[64];
+ gchar t2[64];
+ size_t al, bl;
+ gint r;
+
+ if (!a && !b)
+ return 0;
+
+ if (a && !b)
+ return 1;
+
+ if (b && !a)
+ return -1;
+
+ avahi_unescape_label(t1, sizeof(t1), &a);
+ avahi_unescape_label(t2, sizeof(t2), &b);
+
+ al = strlen(t1);
+ bl = strlen(t2);
+
+ if (al != bl)
+ return al < bl ? -1 : 1;
+
+ if ((r = strcmp(t1, t2)) != 0)
+ return r;
+ }
+}
+
+gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) {
+ g_assert(a);
+ g_assert(b);
+
+ if (a == b)
+ return 0;
+
+/* gchar *t; */
+
+/* g_message("comparing [%s]", t = avahi_record_to_string(a)); */
+/* g_free(t); */
+
+/* g_message("and [%s]", t = avahi_record_to_string(b)); */
+/* g_free(t); */
+
+ if (a->key->class < b->key->class)
+ return -1;
+ else if (a->key->class > b->key->class)
+ return 1;
+
+ if (a->key->type < b->key->type)
+ return -1;
+ else if (a->key->type > b->key->type)
+ return 1;
+
+ switch (a->key->type) {
+
+ case AVAHI_DNS_TYPE_PTR:
+ case AVAHI_DNS_TYPE_CNAME:
+ return lexicographical_domain_cmp(a->data.ptr.name, b->data.ptr.name);
+
+ case AVAHI_DNS_TYPE_SRV: {
+ gint r;
+ 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 = lexicographical_domain_cmp(a->data.srv.name, b->data.srv.name);
+
+ return r;
+ }
+
+ case AVAHI_DNS_TYPE_HINFO: {
+ size_t al = strlen(a->data.hinfo.cpu), bl = strlen(b->data.hinfo.cpu);
+ gint r;
+
+ if (al != bl)
+ return al < bl ? -1 : 1;
+
+ if ((r = strcmp(a->data.hinfo.cpu, b->data.hinfo.cpu)) != 0)
+ return r;
+
+ al = strlen(a->data.hinfo.os), bl = strlen(b->data.hinfo.os);
+
+ if (al != bl)
+ return al < bl ? -1 : 1;
+
+ if ((r = strcmp(a->data.hinfo.os, b->data.hinfo.os)) != 0)
+ return r;
+
+ return 0;
+
+ }
+
+ case AVAHI_DNS_TYPE_TXT: {
+
+ guint8 *ma, *mb;
+ guint asize, bsize;
+ gint r;
+
+ ma = g_new(guint8, asize = avahi_string_list_serialize(a->data.txt.string_list, NULL, 0));
+ mb = g_new(guint8, bsize = avahi_string_list_serialize(b->data.txt.string_list, NULL, 0));
+ avahi_string_list_serialize(a->data.txt.string_list, ma, asize);
+ avahi_string_list_serialize(a->data.txt.string_list, mb, bsize);
+
+ r = lexicographical_memcmp(ma, asize, mb, bsize);
+ g_free(ma);
+ g_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);
+ }
+
+}
diff --git a/avahi-core/rr.h b/avahi-core/rr.h
new file mode 100644
index 0000000..2efb850
--- /dev/null
+++ b/avahi-core/rr.h
@@ -0,0 +1,138 @@
+#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.
+***/
+
+#include <glib.h>
+
+#include "strlst.h"
+#include "address.h"
+
+enum {
+ AVAHI_DNS_TYPE_A = 0x01,
+ AVAHI_DNS_TYPE_NS = 0x02,
+ AVAHI_DNS_TYPE_CNAME = 0x05,
+ AVAHI_DNS_TYPE_SOA = 0x06,
+ AVAHI_DNS_TYPE_PTR = 0x0C,
+ AVAHI_DNS_TYPE_HINFO = 0x0D,
+ AVAHI_DNS_TYPE_MX = 0x0F,
+ AVAHI_DNS_TYPE_TXT = 0x10,
+ AVAHI_DNS_TYPE_AAAA = 0x1C,
+ AVAHI_DNS_TYPE_SRV = 0x21,
+ AVAHI_DNS_TYPE_ANY = 0xFF
+};
+
+enum {
+ AVAHI_DNS_CLASS_IN = 0x01,
+ AVAHI_DNS_CACHE_FLUSH = 0x8000,
+ AVAHI_DNS_UNICAST_RESPONSE = 0x8000
+};
+
+#define AVAHI_DEFAULT_TTL (120*60)
+
+typedef struct {
+ guint ref;
+ gchar *name;
+ guint16 class;
+ guint16 type;
+} AvahiKey;
+
+typedef struct {
+ guint ref;
+ AvahiKey *key;
+
+ guint32 ttl;
+
+ union {
+ struct {
+ gpointer data;
+ guint16 size;
+ } generic;
+
+ struct {
+ guint16 priority;
+ guint16 weight;
+ guint16 port;
+ gchar *name;
+ } srv;
+
+ struct {
+ gchar *name;
+ } ptr; /* and cname */
+
+ struct {
+ gchar *cpu;
+ gchar *os;
+ } hinfo;
+
+ struct {
+ AvahiStringList *string_list;
+ } txt;
+
+ struct {
+ AvahiIPv4Address address;
+ } a;
+
+ struct {
+ AvahiIPv6Address address;
+ } aaaa;
+
+ } data;
+
+} AvahiRecord;
+
+AvahiKey *avahi_key_new(const gchar *name, guint16 class, guint16 type);
+AvahiKey *avahi_key_ref(AvahiKey *k);
+void avahi_key_unref(AvahiKey *k);
+
+gboolean avahi_key_equal(const AvahiKey *a, const AvahiKey *b); /* Treat AVAHI_DNS_CLASS_ANY like any other type */
+gboolean avahi_key_pattern_match(const AvahiKey *pattern, const AvahiKey *k); /* If pattern.type is AVAHI_DNS_CLASS_ANY, k.type is ignored */
+
+gboolean avahi_key_is_pattern(const AvahiKey *k);
+
+guint avahi_key_hash(const AvahiKey *k);
+
+AvahiRecord *avahi_record_new(AvahiKey *k);
+AvahiRecord *avahi_record_new_full(const gchar *name, guint16 class, guint16 type);
+AvahiRecord *avahi_record_ref(AvahiRecord *r);
+void avahi_record_unref(AvahiRecord *r);
+
+const gchar *avahi_dns_class_to_string(guint16 class);
+const gchar *avahi_dns_type_to_string(guint16 type);
+
+gchar *avahi_key_to_string(const AvahiKey *k); /* g_free() the result! */
+gchar *avahi_record_to_string(const AvahiRecord *r); /* g_free() the result! */
+
+gboolean avahi_record_equal_no_ttl(const AvahiRecord *a, const AvahiRecord *b);
+
+AvahiRecord *avahi_record_copy(AvahiRecord *r);
+
+/* returns a maximum estimate for the space that is needed to store
+ * this key in a DNS packet */
+guint avahi_key_get_estimate_size(AvahiKey *k);
+
+/* ditto */
+guint avahi_record_get_estimate_size(AvahiRecord *r);
+
+gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b);
+
+#endif
diff --git a/avahi-core/server.c b/avahi-core/server.c
new file mode 100644
index 0000000..033622d
--- /dev/null
+++ b/avahi-core/server.c
@@ -0,0 +1,1035 @@
+/* $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/socket.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include "server.h"
+#include "util.h"
+#include "iface.h"
+#include "socket.h"
+#include "subscribe.h"
+
+static void free_entry(AvahiServer*s, AvahiEntry *e) {
+ AvahiEntry *t;
+
+ g_assert(s);
+ g_assert(e);
+
+ avahi_goodbye_entry(s, e, TRUE);
+
+ /* Remove from linked list */
+ AVAHI_LLIST_REMOVE(AvahiEntry, entries, s->entries, e);
+
+ /* Remove from hash table indexed by name */
+ t = g_hash_table_lookup(s->entries_by_key, e->record->key);
+ AVAHI_LLIST_REMOVE(AvahiEntry, by_key, t, e);
+ if (t)
+ g_hash_table_replace(s->entries_by_key, t->record->key, t);
+ else
+ g_hash_table_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);
+ g_free(e);
+}
+
+static void free_group(AvahiServer *s, AvahiEntryGroup *g) {
+ g_assert(s);
+ g_assert(g);
+
+ while (g->entries)
+ free_entry(s, g->entries);
+
+ AVAHI_LLIST_REMOVE(AvahiEntryGroup, groups, s->groups, g);
+ g_free(g);
+}
+
+static void cleanup_dead(AvahiServer *s) {
+ AvahiEntryGroup *g, *ng;
+ AvahiEntry *e, *ne;
+ g_assert(s);
+
+
+ if (s->need_group_cleanup) {
+ for (g = s->groups; g; g = ng) {
+ ng = g->groups_next;
+
+ if (g->dead)
+ free_group(s, g);
+ }
+
+ s->need_group_cleanup = FALSE;
+ }
+
+ if (s->need_entry_cleanup) {
+ for (e = s->entries; e; e = ne) {
+ ne = e->entries_next;
+
+ if (e->dead)
+ free_entry(s, e);
+ }
+
+ s->need_entry_cleanup = FALSE;
+ }
+}
+
+static void handle_query_key(AvahiServer *s, AvahiKey *k, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast, gboolean unicast_response) {
+ AvahiEntry *e;
+ gchar *txt;
+
+ g_assert(s);
+ g_assert(k);
+ g_assert(i);
+ g_assert(a);
+
+ g_message("Handling query: %s", txt = avahi_key_to_string(k));
+ g_free(txt);
+
+ avahi_packet_scheduler_incoming_query(i->scheduler, k);
+
+ if (k->type == AVAHI_DNS_TYPE_ANY) {
+
+ /* 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_registered(s, e, i))
+ avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, FALSE);
+ } else {
+
+ /* Handle all other queries */
+
+ for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
+ if (!e->dead && avahi_entry_registered(s, e, i))
+ avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, FALSE);
+ }
+}
+
+static void withdraw_entry(AvahiServer *s, AvahiEntry *e) {
+ g_assert(s);
+ g_assert(e);
+
+
+ if (e->group) {
+ AvahiEntry *k;
+
+ for (k = e->group->entries; k; k = k->by_group_next) {
+ avahi_goodbye_entry(s, k, FALSE);
+ k->dead = TRUE;
+ }
+
+ avahi_entry_group_change_state(e->group, AVAHI_ENTRY_GROUP_COLLISION);
+ } else {
+ avahi_goodbye_entry(s, e, FALSE);
+ e->dead = TRUE;
+ }
+
+ s->need_entry_cleanup = TRUE;
+}
+
+static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) {
+ AvahiEntry *e, *n;
+ gchar *t;
+
+ g_assert(s);
+ g_assert(record);
+ g_assert(i);
+
+ t = avahi_record_to_string(record);
+
+/* g_message("PROBE: [%s]", t); */
+
+ for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
+ n = e->by_key_next;
+
+ if (e->dead || avahi_record_equal_no_ttl(record, e->record))
+ continue;
+
+ if (avahi_entry_registering(s, e, i)) {
+ gint cmp;
+
+ if ((cmp = avahi_record_lexicographical_compare(record, e->record)) > 0) {
+ withdraw_entry(s, e);
+ g_message("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
+ } else if (cmp < 0)
+ g_message("Recieved conflicting probe [%s]. Local host won.", t);
+
+ }
+ }
+
+ g_free(t);
+}
+
+static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
+ guint n;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(i);
+ g_assert(a);
+
+ /* Handle the questions */
+ for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT); n > 0; n --) {
+ AvahiKey *key;
+ gboolean unicast_response = FALSE;
+
+ if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
+ g_warning("Packet too short (1)");
+ return;
+ }
+
+ handle_query_key(s, key, i, a, port, legacy_unicast, unicast_response);
+ avahi_key_unref(key);
+ }
+
+ /* Known Answer Suppression */
+ for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) {
+ AvahiRecord *record;
+ gboolean unique = FALSE;
+
+ if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
+ g_warning("Packet too short (2)");
+ return;
+ }
+
+ avahi_packet_scheduler_incoming_known_answer(i->scheduler, 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;
+ gboolean unique = FALSE;
+
+ if (!(record = avahi_dns_packet_consume_record(p, &unique))) {
+ g_warning("Packet too short (3)");
+ return;
+ }
+
+ if (record->key->type != AVAHI_DNS_TYPE_ANY)
+ incoming_probe(s, record, i);
+
+ avahi_record_unref(record);
+ }
+}
+
+static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) {
+ gboolean valid = TRUE;
+ AvahiEntry *e, *n;
+ gchar *t;
+
+ g_assert(s);
+ g_assert(i);
+ g_assert(record);
+
+ t = avahi_record_to_string(record);
+
+/* g_message("CHECKING FOR CONFLICT: [%s]", t); */
+
+ for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
+ n = e->by_key_next;
+
+ if (e->dead)
+ continue;
+
+ if (avahi_entry_registered(s, e, i)) {
+
+ gboolean equal = avahi_record_equal_no_ttl(record, e->record);
+
+ /* Check whether there is a unique record conflict */
+ if (!equal && ((e->flags & AVAHI_ENTRY_UNIQUE) || unique)) {
+ gint cmp;
+
+ /* The lexicographically later data wins. */
+ if ((cmp = avahi_record_lexicographical_compare(record, e->record)) > 0) {
+ g_message("Recieved conflicting record [%s]. Local host lost. Withdrawing.", t);
+ withdraw_entry(s, e);
+ } else if (cmp < 0) {
+ /* Tell the other host that our entry is lexicographically later */
+
+ g_message("Recieved conflicting record [%s]. Local host won. Refreshing.", t);
+
+ valid = FALSE;
+ avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE);
+ }
+
+ /* Check wheter there is a TTL conflict */
+ } else if (equal && record->ttl <= e->record->ttl/2) {
+ /* Correct the TTL */
+ valid = FALSE;
+ avahi_interface_post_response(i, a, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE);
+ g_message("Recieved record with bad TTL [%s]. Refreshing.", t);
+ }
+
+ } else if (avahi_entry_registering(s, e, i)) {
+
+ if (!avahi_record_equal_no_ttl(record, e->record) && ((e->flags & AVAHI_ENTRY_UNIQUE) || unique)) {
+
+ /* We are currently registering a matching record, but
+ * someone else already claimed it, so let's
+ * withdraw */
+
+ g_message("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
+ withdraw_entry(s, e);
+ }
+ }
+ }
+
+ g_free(t);
+
+ return valid;
+}
+
+static void handle_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) {
+ guint n;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(i);
+ g_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;
+ gboolean cache_flush = FALSE;
+ gchar *txt;
+
+ if (!(record = avahi_dns_packet_consume_record(p, &cache_flush))) {
+ g_warning("Packet too short (4)");
+ return;
+ }
+
+ if (record->key->type != AVAHI_DNS_TYPE_ANY) {
+
+ g_message("Handling response: %s", txt = avahi_record_to_string(record));
+ g_free(txt);
+
+ if (handle_conflict(s, i, record, cache_flush, a)) {
+ avahi_cache_update(i->cache, record, cache_flush, a);
+ avahi_packet_scheduler_incoming_response(i->scheduler, record);
+ }
+ }
+
+ avahi_record_unref(record);
+ }
+}
+
+static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, struct sockaddr *sa, gint iface, gint ttl) {
+ AvahiInterface *i;
+ AvahiAddress a;
+ guint16 port;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(sa);
+ g_assert(iface > 0);
+
+ if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family))) {
+ g_warning("Recieved packet from invalid interface.");
+ return;
+ }
+
+ g_message("new packet recieved on interface '%s.%i'.", i->hardware->name, i->protocol);
+
+ if (sa->sa_family == AF_INET6) {
+ static const guint8 ipv4_in_ipv6[] = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF };
+
+ /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
+
+ if (memcmp(((struct sockaddr_in6*) sa)->sin6_addr.s6_addr, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0)
+ return;
+ }
+
+ if (avahi_dns_packet_check_valid(p) < 0) {
+ g_warning("Recieved invalid packet.");
+ return;
+ }
+
+ port = avahi_port_from_sockaddr(sa);
+ avahi_address_from_sockaddr(sa, &a);
+
+ if (avahi_dns_packet_is_query(p)) {
+ gboolean legacy_unicast = FALSE;
+
+ if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_QDCOUNT) == 0 ||
+ avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) {
+ g_warning("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)) {
+ g_warning("Invalid legacy unicast query packet.");
+ return;
+ }
+
+ legacy_unicast = TRUE;
+ }
+
+ handle_query(s, p, i, &a, port, legacy_unicast);
+
+ g_message("Handled query");
+ } else {
+
+ if (port != AVAHI_MDNS_PORT) {
+ g_warning("Recieved repsonse with invalid source port %u on interface '%s.%i'", port, i->hardware->name, i->protocol);
+ return;
+ }
+
+ if (ttl != 255) {
+ g_warning("Recieved response with invalid TTL %u on interface '%s.%i'.", ttl, i->hardware->name, i->protocol);
+ if (!s->ignore_bad_ttl)
+ 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) {
+ g_warning("Invalid response packet.");
+ return;
+ }
+
+ handle_response(s, p, i, &a);
+ g_message("Handled response");
+ }
+}
+
+static void work(AvahiServer *s) {
+ struct sockaddr_in6 sa6;
+ struct sockaddr_in sa;
+ AvahiDnsPacket *p;
+ gint iface = -1;
+ guint8 ttl;
+
+ g_assert(s);
+
+ if (s->pollfd_ipv4.revents & G_IO_IN) {
+ if ((p = avahi_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
+ dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
+ avahi_dns_packet_free(p);
+ }
+ }
+
+ if (s->pollfd_ipv6.revents & G_IO_IN) {
+ if ((p = avahi_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
+ dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
+ avahi_dns_packet_free(p);
+ }
+ }
+}
+
+static gboolean prepare_func(GSource *source, gint *timeout) {
+ g_assert(source);
+ g_assert(timeout);
+
+ *timeout = -1;
+ return FALSE;
+}
+
+static gboolean check_func(GSource *source) {
+ AvahiServer* s;
+ g_assert(source);
+
+ s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
+ g_assert(s);
+
+ return (s->pollfd_ipv4.revents | s->pollfd_ipv6.revents) & (G_IO_IN | G_IO_HUP | G_IO_ERR);
+}
+
+static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
+ AvahiServer* s;
+ g_assert(source);
+
+ s = *((AvahiServer**) (((guint8*) source) + sizeof(GSource)));
+ g_assert(s);
+
+ work(s);
+ cleanup_dead(s);
+
+ return TRUE;
+}
+
+static void add_default_entries(AvahiServer *s) {
+ struct utsname utsname;
+ AvahiAddress a;
+ AvahiRecord *r;
+
+ g_assert(s);
+
+ /* Fill in HINFO rr */
+ r = avahi_record_new_full(s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO);
+ uname(&utsname);
+ r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
+ r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
+ avahi_server_add(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, r);
+ avahi_record_unref(r);
+
+ /* Add localhost entries */
+ avahi_address_parse("127.0.0.1", AF_INET, &a);
+ avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE|AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "localhost", &a);
+
+ avahi_address_parse("::1", AF_INET6, &a);
+ avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE|AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
+}
+
+AvahiServer *avahi_server_new(GMainContext *c) {
+ gchar *hn;
+ AvahiServer *s;
+
+ static GSourceFuncs source_funcs = {
+ prepare_func,
+ check_func,
+ dispatch_func,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ s = g_new(AvahiServer, 1);
+
+ s->ignore_bad_ttl = FALSE;
+ s->need_entry_cleanup = s->need_group_cleanup = FALSE;
+
+ s->fd_ipv4 = avahi_open_socket_ipv4();
+ s->fd_ipv6 = avahi_open_socket_ipv6();
+
+ if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
+ g_critical("Failed to create IP sockets.\n");
+ g_free(s);
+ return NULL;
+ }
+
+ if (s->fd_ipv4 < 0)
+ g_message("Failed to create IPv4 socket, proceeding in IPv6 only mode");
+ else if (s->fd_ipv6 < 0)
+ g_message("Failed to create IPv6 socket, proceeding in IPv4 only mode");
+
+ if (c)
+ g_main_context_ref(s->context = c);
+ else
+ s->context = g_main_context_default();
+
+ AVAHI_LLIST_HEAD_INIT(AvahiEntry, s->entries);
+ s->entries_by_key = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
+ AVAHI_LLIST_HEAD_INIT(AvahiGroup, s->groups);
+
+ AVAHI_LLIST_HEAD_INIT(AvahiSubscription, s->subscriptions);
+ s->subscription_hashtable = g_hash_table_new((GHashFunc) avahi_key_hash, (GEqualFunc) avahi_key_equal);
+
+ /* Get host name */
+ hn = avahi_get_host_name();
+ hn[strcspn(hn, ".")] = 0;
+
+ s->hostname = g_strdup_printf("%s.local.", hn);
+ g_free(hn);
+
+ s->time_event_queue = avahi_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
+ s->monitor = avahi_interface_monitor_new(s);
+ avahi_interface_monitor_sync(s->monitor);
+ add_default_entries(s);
+
+ /* Prepare IO source registration */
+ s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(AvahiServer*));
+ *((AvahiServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
+
+ memset(&s->pollfd_ipv4, 0, sizeof(s->pollfd_ipv4));
+ s->pollfd_ipv4.fd = s->fd_ipv4;
+ s->pollfd_ipv4.events = G_IO_IN|G_IO_ERR|G_IO_HUP;
+ g_source_add_poll(s->source, &s->pollfd_ipv4);
+
+ memset(&s->pollfd_ipv6, 0, sizeof(s->pollfd_ipv6));
+ s->pollfd_ipv6.fd = s->fd_ipv6;
+ s->pollfd_ipv6.events = G_IO_IN|G_IO_ERR|G_IO_HUP;
+ g_source_add_poll(s->source, &s->pollfd_ipv6);
+
+ g_source_attach(s->source, s->context);
+
+ return s;
+}
+
+void avahi_server_free(AvahiServer* s) {
+ g_assert(s);
+
+ while(s->entries)
+ free_entry(s, s->entries);
+
+ avahi_interface_monitor_free(s->monitor);
+
+ while (s->groups)
+ free_group(s, s->groups);
+
+ while (s->subscriptions)
+ avahi_subscription_free(s->subscriptions);
+ g_hash_table_destroy(s->subscription_hashtable);
+
+ g_hash_table_destroy(s->entries_by_key);
+
+ avahi_time_event_queue_free(s->time_event_queue);
+
+ if (s->fd_ipv4 >= 0)
+ close(s->fd_ipv4);
+ if (s->fd_ipv6 >= 0)
+ close(s->fd_ipv6);
+
+ g_free(s->hostname);
+
+ g_source_destroy(s->source);
+ g_source_unref(s->source);
+ g_main_context_unref(s->context);
+
+ g_free(s);
+}
+
+void avahi_server_add(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ AvahiRecord *r) {
+
+ AvahiEntry *e, *t;
+ g_assert(s);
+ g_assert(r);
+
+ g_assert(r->key->type != AVAHI_DNS_TYPE_ANY);
+
+ e = g_new(AvahiEntry, 1);
+ e->server = s;
+ e->record = avahi_record_ref(r);
+ e->group = g;
+ e->interface = interface;
+ e->protocol = protocol;
+ e->flags = flags;
+ e->dead = FALSE;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, e->announcements);
+
+ AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
+
+ /* Insert into hash table indexed by name */
+ t = g_hash_table_lookup(s->entries_by_key, e->record->key);
+ AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
+ g_hash_table_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);
+}
+const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state) {
+ AvahiEntry **e = (AvahiEntry**) state;
+ g_assert(s);
+ g_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);
+}
+
+void avahi_server_dump(AvahiServer *s, FILE *f) {
+ AvahiEntry *e;
+ g_assert(s);
+ g_assert(f);
+
+ fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
+
+ for (e = s->entries; e; e = e->entries_next) {
+ gchar *t;
+
+ if (e->dead)
+ continue;
+
+ t = avahi_record_to_string(e->record);
+ fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
+ g_free(t);
+ }
+
+ avahi_dump_caches(s->monitor, f);
+}
+
+void avahi_server_add_ptr(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ const gchar *dest) {
+
+ AvahiRecord *r;
+
+ g_assert(dest);
+
+ r = avahi_record_new_full(name ? name : s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR);
+ r->data.ptr.name = avahi_normalize_name(dest);
+ avahi_server_add(s, g, interface, protocol, flags, r);
+ avahi_record_unref(r);
+}
+
+void avahi_server_add_address(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ AvahiAddress *a) {
+
+ gchar *n = NULL;
+ g_assert(s);
+ g_assert(a);
+
+ name = name ? (n = avahi_normalize_name(name)) : s->hostname;
+
+ if (a->family == AF_INET) {
+ gchar *reverse;
+ AvahiRecord *r;
+
+ r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A);
+ r->data.a.address = a->data.ipv4;
+ avahi_server_add(s, g, interface, protocol, flags, r);
+ avahi_record_unref(r);
+
+ reverse = avahi_reverse_lookup_name_ipv4(&a->data.ipv4);
+ g_assert(reverse);
+ avahi_server_add_ptr(s, g, interface, protocol, flags, reverse, name);
+ g_free(reverse);
+
+ } else {
+ gchar *reverse;
+ AvahiRecord *r;
+
+ r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA);
+ r->data.aaaa.address = a->data.ipv6;
+ avahi_server_add(s, g, interface, protocol, flags, r);
+ avahi_record_unref(r);
+
+ reverse = avahi_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
+ g_assert(reverse);
+ avahi_server_add_ptr(s, g, interface, protocol, flags, reverse, name);
+ g_free(reverse);
+
+ reverse = avahi_reverse_lookup_name_ipv6_int(&a->data.ipv6);
+ g_assert(reverse);
+ avahi_server_add_ptr(s, g, interface, protocol, flags, reverse, name);
+ g_free(reverse);
+ }
+
+ g_free(n);
+}
+
+void avahi_server_add_text_strlst(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ AvahiStringList *strlst) {
+
+ AvahiRecord *r;
+
+ g_assert(s);
+
+ r = avahi_record_new_full(name ? name : s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT);
+ r->data.txt.string_list = strlst;
+ avahi_server_add(s, g, interface, protocol, flags, r);
+ avahi_record_unref(r);
+}
+
+void avahi_server_add_text_va(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ va_list va) {
+
+ g_assert(s);
+
+ avahi_server_add_text_strlst(s, g, interface, protocol, flags, name, avahi_string_list_new_va(va));
+}
+
+void avahi_server_add_text(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ AvahiEntryFlags flags,
+ const gchar *name,
+ ...) {
+
+ va_list va;
+
+ g_assert(s);
+
+ va_start(va, name);
+ avahi_server_add_text_va(s, g, interface, protocol, flags, name, va);
+ va_end(va);
+}
+
+static void escape_service_name(gchar *d, guint size, const gchar *s) {
+ g_assert(d);
+ g_assert(size);
+ g_assert(s);
+
+ while (*s && size >= 2) {
+ if (*s == '.' || *s == '\\') {
+ if (size < 3)
+ break;
+
+ *(d++) = '\\';
+ size--;
+ }
+
+ *(d++) = *(s++);
+ size--;
+ }
+
+ g_assert(size > 0);
+ *(d++) = 0;
+}
+
+void avahi_server_add_service_strlst(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ const gchar *type,
+ const gchar *name,
+ const gchar *domain,
+ const gchar *host,
+ guint16 port,
+ AvahiStringList *strlst) {
+
+ gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
+ AvahiRecord *r;
+
+ g_assert(s);
+ g_assert(type);
+ g_assert(name);
+
+ escape_service_name(ename, sizeof(ename), name);
+
+ if (domain) {
+ while (domain[0] == '.')
+ domain++;
+ } else
+ domain = "local";
+
+ if (!host)
+ host = s->hostname;
+
+ snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
+ snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
+
+ avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, ptr_name, svc_name);
+
+ r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV);
+ r->data.srv.priority = 0;
+ r->data.srv.weight = 0;
+ r->data.srv.port = port;
+ r->data.srv.name = avahi_normalize_name(host);
+ avahi_server_add(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, r);
+ avahi_record_unref(r);
+
+ avahi_server_add_text_strlst(s, g, interface, protocol, AVAHI_ENTRY_UNIQUE, svc_name, strlst);
+
+ snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
+ avahi_server_add_ptr(s, g, interface, protocol, AVAHI_ENTRY_NULL, enum_ptr, ptr_name);
+}
+
+void avahi_server_add_service_va(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ const gchar *type,
+ const gchar *name,
+ const gchar *domain,
+ const gchar *host,
+ guint16 port,
+ va_list va){
+
+ g_assert(s);
+ g_assert(type);
+ g_assert(name);
+
+ avahi_server_add_service(s, g, interface, protocol, type, name, domain, host, port, avahi_string_list_new_va(va));
+}
+
+void avahi_server_add_service(
+ AvahiServer *s,
+ AvahiEntryGroup *g,
+ gint interface,
+ guchar protocol,
+ const gchar *type,
+ const gchar *name,
+ const gchar *domain,
+ const gchar *host,
+ guint16 port,
+ ... ){
+
+ va_list va;
+
+ g_assert(s);
+ g_assert(type);
+ g_assert(name);
+
+ va_start(va, port);
+ avahi_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
+ va_end(va);
+}
+
+static void post_query_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
+ AvahiKey *k = userdata;
+
+ g_assert(m);
+ g_assert(i);
+ g_assert(k);
+
+ avahi_interface_post_query(i, k, FALSE);
+}
+
+void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key) {
+ g_assert(s);
+ g_assert(key);
+
+ avahi_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
+}
+
+struct tmpdata {
+ AvahiRecord *record;
+ gboolean flush_cache;
+};
+
+static void post_response_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
+ struct tmpdata *tmpdata = userdata;
+
+ g_assert(m);
+ g_assert(i);
+ g_assert(tmpdata);
+
+ avahi_interface_post_response(i, NULL, tmpdata->record, tmpdata->flush_cache, FALSE);
+}
+
+void avahi_server_post_response(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *record, gboolean flush_cache) {
+ struct tmpdata tmpdata;
+
+ g_assert(s);
+ g_assert(record);
+
+ tmpdata.record = record;
+ tmpdata.flush_cache = flush_cache;
+
+ avahi_interface_monitor_walk(s->monitor, interface, protocol, post_response_callback, &tmpdata);
+}
+
+void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state) {
+ g_assert(g);
+
+ g->state = state;
+
+ if (g->callback) {
+ g->callback(g->server, g, state, g->userdata);
+ return;
+ }
+}
+
+AvahiEntryGroup *avahi_entry_group_new(AvahiServer *s, AvahiEntryGroupCallback callback, gpointer userdata) {
+ AvahiEntryGroup *g;
+
+ g_assert(s);
+
+ g = g_new(AvahiEntryGroup, 1);
+ g->server = s;
+ g->callback = callback;
+ g->userdata = userdata;
+ g->dead = FALSE;
+ g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
+ g->n_probing = 0;
+ AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
+
+ AVAHI_LLIST_PREPEND(AvahiEntryGroup, groups, s->groups, g);
+ return g;
+}
+
+void avahi_entry_group_free(AvahiEntryGroup *g) {
+ g_assert(g);
+ g_assert(g->server);
+
+ g->dead = TRUE;
+ g->server->need_group_cleanup = TRUE;
+}
+
+void avahi_entry_group_commit(AvahiEntryGroup *g) {
+ g_assert(g);
+ g_assert(!g->dead);
+
+ if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)
+ return;
+
+ avahi_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
+ avahi_announce_group(g->server, g);
+ avahi_entry_group_check_probed(g, FALSE);
+}
+
+gboolean avahi_entry_commited(AvahiEntry *e) {
+ g_assert(e);
+ g_assert(!e->dead);
+
+ return !e->group ||
+ e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
+ e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
+}
+
+AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) {
+ g_assert(g);
+ g_assert(!g->dead);
+
+ return g->state;
+}
diff --git a/avahi-core/server.h b/avahi-core/server.h
new file mode 100644
index 0000000..efe8ed9
--- /dev/null
+++ b/avahi-core/server.h
@@ -0,0 +1,100 @@
+#ifndef fooserverhfoo
+#define fooserverhfoo
+
+/* $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.h"
+#include "iface.h"
+#include "prioq.h"
+#include "llist.h"
+#include "timeeventq.h"
+#include "announce.h"
+#include "subscribe.h"
+
+struct _AvahiEntry {
+ AvahiServer *server;
+ AvahiEntryGroup *group;
+
+ gboolean dead;
+
+ AvahiEntryFlags flags;
+ AvahiRecord *record;
+ gint interface;
+ guchar protocol;
+
+ AVAHI_LLIST_FIELDS(AvahiEntry, entries);
+ AVAHI_LLIST_FIELDS(AvahiEntry, by_key);
+ AVAHI_LLIST_FIELDS(AvahiEntry, by_group);
+
+ AVAHI_LLIST_HEAD(AvahiAnnouncement, announcements);
+};
+
+struct _AvahiEntryGroup {
+ AvahiServer *server;
+ gboolean dead;
+
+ AvahiEntryGroupState state;
+ gpointer userdata;
+ AvahiEntryGroupCallback callback;
+
+ guint n_probing;
+
+ AVAHI_LLIST_FIELDS(AvahiEntryGroup, groups);
+ AVAHI_LLIST_HEAD(AvahiEntry, entries);
+};
+
+struct _AvahiServer {
+ GMainContext *context;
+ AvahiInterfaceMonitor *monitor;
+
+ AVAHI_LLIST_HEAD(AvahiEntry, entries);
+ GHashTable *entries_by_key;
+
+ AVAHI_LLIST_HEAD(AvahiEntryGroup, groups);
+
+ AVAHI_LLIST_HEAD(AvahiSubscription, subscriptions);
+ GHashTable *subscription_hashtable;
+
+ gboolean need_entry_cleanup, need_group_cleanup;
+
+ AvahiTimeEventQueue *time_event_queue;
+
+ gchar *hostname;
+
+ gint fd_ipv4, fd_ipv6;
+
+ GPollFD pollfd_ipv4, pollfd_ipv6;
+ GSource *source;
+
+ gboolean ignore_bad_ttl;
+};
+
+gboolean avahi_server_entry_match_interface(AvahiEntry *e, AvahiInterface *i);
+
+void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, AvahiKey *key);
+void avahi_server_post_response(AvahiServer *s, gint interface, guchar protocol, AvahiRecord *record, gboolean flush_cache);
+
+void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state);
+
+gboolean avahi_entry_commited(AvahiEntry *e);
+
+#endif
diff --git a/avahi-core/socket.c b/avahi-core/socket.c
new file mode 100644
index 0000000..e59c78a
--- /dev/null
+++ b/avahi-core/socket.c
@@ -0,0 +1,511 @@
+/* $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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "dns.h"
+#include "util.h"
+#include "socket.h"
+
+static void mdns_mcast_group_ipv4(struct sockaddr_in *ret_sa) {
+ g_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, "224.0.0.251", &ret_sa->sin_addr);
+}
+
+static void mdns_mcast_group_ipv6(struct sockaddr_in6 *ret_sa) {
+
+ g_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, "ff02::fb", &ret_sa->sin6_addr);
+}
+
+int avahi_mdns_mcast_join_ipv4 (int index, int fd)
+{
+ struct ip_mreqn mreq;
+ struct sockaddr_in sa;
+
+ mdns_mcast_group_ipv4 (&sa);
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr = sa.sin_addr;
+ mreq.imr_ifindex = index;
+
+ if (setsockopt(fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+ g_warning("IP_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int avahi_mdns_mcast_join_ipv6 (int index, int fd)
+{
+ struct ipv6_mreq mreq6;
+ struct sockaddr_in6 sa6;
+
+ mdns_mcast_group_ipv6 (&sa6);
+
+ memset(&mreq6, 0, sizeof(mreq6));
+ mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
+ mreq6.ipv6mr_interface = index;
+
+ if (setsockopt(fd, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
+ g_warning("IPV6_ADD_MEMBERSHIP failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int avahi_mdns_mcast_leave_ipv4 (int index, int fd)
+{
+ struct ip_mreqn mreq;
+ struct sockaddr_in sa;
+
+ mdns_mcast_group_ipv4 (&sa);
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr = sa.sin_addr;
+ mreq.imr_ifindex = index;
+
+ if (setsockopt(fd, SOL_IP, IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
+ g_warning("IP_DROP_MEMBERSHIP failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int avahi_mdns_mcast_leave_ipv6 (int index, int fd)
+{
+ struct ipv6_mreq mreq6;
+ struct sockaddr_in6 sa6;
+
+ mdns_mcast_group_ipv6 (&sa6);
+
+ memset(&mreq6, 0, sizeof(mreq6));
+ mreq6.ipv6mr_multiaddr = sa6.sin6_addr;
+ mreq6.ipv6mr_interface = index;
+
+ if (setsockopt(fd, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq6, sizeof(mreq6)) < 0) {
+ g_warning("IPV6_DROP_MEMBERSHIP failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+gint avahi_open_socket_ipv4(void) {
+ struct sockaddr_in local;
+ int fd = -1, ttl, yes;
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ g_warning("socket() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ ttl = 255;
+ if (setsockopt(fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) {
+ g_warning("IP_MULTICAST_TTL failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ ttl = 255;
+ if (setsockopt(fd, SOL_IP, IP_TTL, &ttl, sizeof(ttl)) < 0) {
+ g_warning("IP_TTL failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
+ g_warning("SO_REUSEADDR failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, SOL_IP, IP_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
+ g_warning("IP_MULTICAST_LOOP failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+
+ memset(&local, 0, sizeof(local));
+ local.sin_family = AF_INET;
+ local.sin_port = htons(AVAHI_MDNS_PORT);
+
+ if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
+ g_warning("bind() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, SOL_IP, IP_RECVTTL, &yes, sizeof(yes)) < 0) {
+ g_warning("IP_RECVTTL failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, SOL_IP, IP_PKTINFO, &yes, sizeof(yes)) < 0) {
+ g_warning("IP_PKTINFO failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (avahi_set_cloexec(fd) < 0) {
+ g_warning("FD_CLOEXEC failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (avahi_set_nonblock(fd) < 0) {
+ g_warning("O_NONBLOCK failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+gint avahi_open_socket_ipv6(void) {
+ struct sockaddr_in6 sa, local;
+ int fd = -1, ttl, yes;
+
+ mdns_mcast_group_ipv6(&sa);
+
+ if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ g_warning("socket() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ ttl = 255;
+ if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
+ g_warning("IPV6_MULTICAST_HOPS failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ ttl = 255;
+ if (setsockopt(fd, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) {
+ g_warning("IPV6_UNICAST_HOPS failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
+ g_warning("SO_REUSEADDR failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)) < 0) {
+ g_warning("IPV6_V6ONLY failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, SOL_IPV6, IPV6_MULTICAST_LOOP, &yes, sizeof(yes)) < 0) {
+ g_warning("IPV6_MULTICAST_LOOP failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ memset(&local, 0, sizeof(local));
+ local.sin6_family = AF_INET6;
+ local.sin6_port = htons(AVAHI_MDNS_PORT);
+
+ if (bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
+ g_warning("bind() failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, SOL_IPV6, IPV6_HOPLIMIT, &yes, sizeof(yes)) < 0) {
+ g_warning("IPV6_HOPLIMIT failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ yes = 1;
+ if (setsockopt(fd, SOL_IPV6, IPV6_PKTINFO, &yes, sizeof(yes)) < 0) {
+ g_warning("IPV6_PKTINFO failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (avahi_set_cloexec(fd) < 0) {
+ g_warning("FD_CLOEXEC failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (avahi_set_nonblock(fd) < 0) {
+ g_warning("O_NONBLOCK failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ return fd;
+
+fail:
+ if (fd >= 0)
+ close(fd);
+
+ return -1;
+}
+
+static gint sendmsg_loop(gint fd, struct msghdr *msg, gint flags) {
+ g_assert(fd >= 0);
+ g_assert(msg);
+
+ for (;;) {
+
+ if (sendmsg(fd, msg, flags) >= 0)
+ break;
+
+ if (errno != EAGAIN) {
+ g_message("sendmsg() failed: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (avahi_wait_for_write(fd) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+gint avahi_send_dns_packet_ipv4(gint fd, gint interface, AvahiDnsPacket *p) {
+ struct sockaddr_in sa;
+ struct msghdr msg;
+ struct iovec io;
+ struct cmsghdr *cmsg;
+ struct in_pktinfo *pkti;
+ uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in_pktinfo)];
+
+ g_assert(fd >= 0);
+ g_assert(p);
+ g_assert(avahi_dns_packet_check_valid(p) >= 0);
+
+ mdns_mcast_group_ipv4(&sa);
+
+ memset(&io, 0, sizeof(io));
+ io.iov_base = AVAHI_DNS_PACKET_DATA(p);
+ io.iov_len = p->size;
+
+ memset(cmsg_data, 0, sizeof(cmsg_data));
+ cmsg = (struct cmsghdr*) cmsg_data;
+ cmsg->cmsg_len = sizeof(cmsg_data);
+ cmsg->cmsg_level = IPPROTO_IP;
+ cmsg->cmsg_type = IP_PKTINFO;
+
+ pkti = (struct in_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
+ pkti->ipi_ifindex = interface;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_iov = &io;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_data;
+ msg.msg_controllen = sizeof(cmsg_data);
+ msg.msg_flags = 0;
+
+ return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
+}
+
+gint avahi_send_dns_packet_ipv6(gint fd, gint interface, AvahiDnsPacket *p) {
+ struct sockaddr_in6 sa;
+ struct msghdr msg;
+ struct iovec io;
+ struct cmsghdr *cmsg;
+ struct in6_pktinfo *pkti;
+ uint8_t cmsg_data[sizeof(struct cmsghdr) + sizeof(struct in6_pktinfo)];
+
+ g_assert(fd >= 0);
+ g_assert(p);
+ g_assert(avahi_dns_packet_check_valid(p) >= 0);
+
+ mdns_mcast_group_ipv6(&sa);
+
+ memset(&io, 0, sizeof(io));
+ io.iov_base = AVAHI_DNS_PACKET_DATA(p);
+ io.iov_len = p->size;
+
+ memset(cmsg_data, 0, sizeof(cmsg_data));
+ cmsg = (struct cmsghdr*) cmsg_data;
+ cmsg->cmsg_len = sizeof(cmsg_data);
+ cmsg->cmsg_level = IPPROTO_IPV6;
+ cmsg->cmsg_type = IPV6_PKTINFO;
+
+ pkti = (struct in6_pktinfo*) (cmsg_data + sizeof(struct cmsghdr));
+ pkti->ipi6_ifindex = interface;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &sa;
+ msg.msg_namelen = sizeof(sa);
+ msg.msg_iov = &io;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_data;
+ msg.msg_controllen = sizeof(cmsg_data);
+ msg.msg_flags = 0;
+
+ return sendmsg_loop(fd, &msg, MSG_DONTROUTE);
+}
+
+AvahiDnsPacket* avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in *ret_sa, gint *ret_iface, guint8* ret_ttl) {
+ AvahiDnsPacket *p= NULL;
+ struct msghdr msg;
+ struct iovec io;
+ uint8_t aux[64];
+ ssize_t l;
+ struct cmsghdr *cmsg;
+ gboolean found_ttl = FALSE, found_iface = FALSE;
+
+ g_assert(fd >= 0);
+ g_assert(ret_sa);
+ g_assert(ret_iface);
+ g_assert(ret_ttl);
+
+ p = avahi_dns_packet_new(0);
+
+ io.iov_base = AVAHI_DNS_PACKET_DATA(p);
+ io.iov_len = p->max_size;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = ret_sa;
+ msg.msg_namelen = sizeof(struct sockaddr_in);
+ 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)
+ goto fail;
+
+ p->size = (size_t) l;
+
+ *ret_ttl = 0;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg,cmsg)) {
+ if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) {
+ *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg);
+ found_ttl = TRUE;
+ }
+
+ if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_PKTINFO) {
+ *ret_iface = ((struct in_pktinfo*) CMSG_DATA(cmsg))->ipi_ifindex;
+ found_iface = TRUE;
+ }
+ }
+
+ g_assert(found_iface);
+ g_assert(found_ttl);
+
+ return p;
+
+fail:
+ if (p)
+ avahi_dns_packet_free(p);
+
+ return NULL;
+}
+
+AvahiDnsPacket* avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6 *ret_sa, gint *ret_iface, guint8* ret_ttl) {
+ AvahiDnsPacket *p = NULL;
+ struct msghdr msg;
+ struct iovec io;
+ uint8_t aux[64];
+ ssize_t l;
+ struct cmsghdr *cmsg;
+ gboolean found_ttl = FALSE, found_iface = FALSE;
+
+ g_assert(fd >= 0);
+ g_assert(ret_sa);
+ g_assert(ret_iface);
+ g_assert(ret_ttl);
+
+ p = avahi_dns_packet_new(0);
+
+ io.iov_base = AVAHI_DNS_PACKET_DATA(p);
+ io.iov_len = p->max_size;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = ret_sa;
+ msg.msg_namelen = sizeof(struct sockaddr_in6);
+ 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)
+ goto fail;
+
+ p->size = (size_t) l;
+
+ *ret_ttl = 0;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_HOPLIMIT) {
+ *ret_ttl = *(uint8_t *) CMSG_DATA(cmsg);
+ found_ttl = TRUE;
+ }
+
+ if (cmsg->cmsg_level == SOL_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
+ *ret_iface = ((struct in6_pktinfo*) CMSG_DATA(cmsg))->ipi6_ifindex;
+ found_iface = TRUE;
+ }
+ }
+
+ g_assert(found_iface);
+ g_assert(found_ttl);
+
+ return p;
+
+fail:
+ if (p)
+ avahi_dns_packet_free(p);
+
+ return NULL;
+}
+
diff --git a/avahi-core/socket.h b/avahi-core/socket.h
new file mode 100644
index 0000000..4f6c5a3
--- /dev/null
+++ b/avahi-core/socket.h
@@ -0,0 +1,46 @@
+#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 <netinet/in.h>
+
+#include "dns.h"
+
+#define AVAHI_MDNS_PORT 5353
+
+gint avahi_open_socket_ipv4(void);
+gint avahi_open_socket_ipv6(void);
+
+gint avahi_send_dns_packet_ipv4(gint fd, gint iface, AvahiDnsPacket *p);
+gint avahi_send_dns_packet_ipv6(gint fd, gint iface, AvahiDnsPacket *p);
+
+AvahiDnsPacket *avahi_recv_dns_packet_ipv4(gint fd, struct sockaddr_in*ret_sa, gint *ret_iface, guint8 *ret_ttl);
+AvahiDnsPacket *avahi_recv_dns_packet_ipv6(gint fd, struct sockaddr_in6*ret_sa, gint *ret_iface, guint8 *ret_ttl);
+
+int avahi_mdns_mcast_join_ipv4(int index, int fd);
+int avahi_mdns_mcast_join_ipv6(int index, int fd);
+
+int avahi_mdns_mcast_leave_ipv4(int index, int fd);
+int avahi_mdns_mcast_leave_ipv6(int index, int fd);
+
+#endif
diff --git a/avahi-core/strlst-test.c b/avahi-core/strlst-test.c
new file mode 100644
index 0000000..ebbf194
--- /dev/null
+++ b/avahi-core/strlst-test.c
@@ -0,0 +1,83 @@
+/* $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 <glib.h>
+#include <stdio.h>
+
+#include "strlst.h"
+
+int main(int argc, char *argv[]) {
+ gchar *t;
+ guint8 data[1024];
+ AvahiStringList *a = NULL, *b;
+ guint size, n;
+
+ a = avahi_string_list_add(a, "start");
+ a = avahi_string_list_add(a, "foo");
+ a = avahi_string_list_add(a, "bar");
+ a = avahi_string_list_add(a, "quux");
+ a = avahi_string_list_add_arbitrary(a, (guint8*) "null\0null", 9);
+ a = avahi_string_list_add(a, "end");
+
+ t = avahi_string_list_to_string(a);
+ printf("--%s--\n", t);
+ g_free(t);
+
+ size = avahi_string_list_serialize(a, data, sizeof(data));
+
+ printf("%u\n", size);
+
+ for (t = (gchar*) data, n = 0; n < size; n++, t++) {
+ if (*t <= 32)
+ printf("(%u)", *t);
+ else
+ printf("%c", *t);
+ }
+
+ printf("\n");
+
+ b = avahi_string_list_parse(data, size);
+
+ g_assert(avahi_string_list_equal(a, b));
+
+ t = avahi_string_list_to_string(b);
+ printf("--%s--\n", t);
+ g_free(t);
+
+ avahi_string_list_free(b);
+
+ b = avahi_string_list_copy(a);
+
+ g_assert(avahi_string_list_equal(a, b));
+
+ t = avahi_string_list_to_string(b);
+ printf("--%s--\n", t);
+ g_free(t);
+
+ avahi_string_list_free(a);
+ avahi_string_list_free(b);
+
+ return 0;
+}
diff --git a/avahi-core/strlst.c b/avahi-core/strlst.c
new file mode 100644
index 0000000..ef9fa3e
--- /dev/null
+++ b/avahi-core/strlst.c
@@ -0,0 +1,243 @@
+/* $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 <stdarg.h>
+
+#include "strlst.h"
+
+AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const guint8*text, guint size) {
+ AvahiStringList *n;
+
+ g_assert(text);
+
+ n = g_malloc(sizeof(AvahiStringList) + size);
+ n->next = l;
+ memcpy(n->text, text, n->size = size);
+
+ return n;
+}
+
+AvahiStringList *avahi_string_list_add(AvahiStringList *l, const gchar *text) {
+ g_assert(text);
+
+ return avahi_string_list_add_arbitrary(l, (const guint8*) text, strlen(text));
+}
+
+AvahiStringList *avahi_string_list_parse(gconstpointer data, guint size) {
+ AvahiStringList *r = NULL;
+ const guint8 *c;
+ g_assert(data);
+
+ c = data;
+ for (;;) {
+ guint k;
+
+ if (size < 1)
+ break;
+
+ k = *(c++);
+ r = avahi_string_list_add_arbitrary(r, c, k);
+ c += k;
+
+ size -= 1 + k;
+ }
+
+ return r;
+}
+
+void avahi_string_list_free(AvahiStringList *l) {
+ AvahiStringList *n;
+
+ while (l) {
+ n = l->next;
+ g_free(l);
+ l = n;
+ }
+}
+
+static AvahiStringList* string_list_reverse(AvahiStringList *l) {
+ AvahiStringList *r = NULL, *n;
+
+ while (l) {
+ n = l->next;
+ l->next = r;
+ r = l;
+ l = n;
+ }
+
+ return r;
+}
+
+gchar* avahi_string_list_to_string(AvahiStringList *l) {
+ AvahiStringList *n;
+ guint s = 0;
+ gchar *t, *e;
+
+ l = string_list_reverse(l);
+
+ for (n = l; n; n = n->next) {
+ if (n != l)
+ s ++;
+
+ s += n->size+3;
+ }
+
+ t = e = g_new(gchar, s);
+
+ for (n = l; n; n = n->next) {
+ if (n != l)
+ *(e++) = ' ';
+
+ *(e++) = '"';
+ strncpy(e, (gchar*) n->text, n->size);
+ e[n->size] = 0;
+ e = strchr(e, 0);
+ *(e++) = '"';
+ }
+
+ l = string_list_reverse(l);
+
+ *e = 0;
+
+ return t;
+}
+
+guint avahi_string_list_serialize(AvahiStringList *l, gpointer data, guint size) {
+ guint used = 0;
+
+ if (data) {
+ guint8 *c;
+ AvahiStringList *n;
+
+ g_assert(data);
+
+ l = string_list_reverse(l);
+ c = data;
+
+ for (n = l; n; n = n->next) {
+ guint k;
+ if (size < 1)
+ break;
+
+ k = n->size;
+ if (k > 255)
+ k = 255;
+
+ if (k > size-1)
+ k = size-1;
+
+ *(c++) = k;
+ memcpy(c, n->text, k);
+ c += k;
+
+ used += 1+ k;
+ }
+
+ l = string_list_reverse(l);
+ } else {
+ AvahiStringList *n;
+
+ for (n = l; n; n = n->next) {
+ guint k;
+
+ k = n->size;
+ if (k > 255)
+ k = 255;
+
+ used += 1+k;
+ }
+ }
+
+ return used;
+}
+
+gboolean avahi_string_list_equal(AvahiStringList *a, AvahiStringList *b) {
+
+ for (;;) {
+ if (!a && !b)
+ return TRUE;
+
+ if (!a || !b)
+ return FALSE;
+
+ if (a->size != b->size)
+ return FALSE;
+
+ if (a->size != 0 && memcmp(a->text, b->text, a->size) != 0)
+ return FALSE;
+
+ a = a->next;
+ b = b->next;
+ }
+}
+
+AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...) {
+ va_list va;
+
+ va_start(va, r);
+ r = avahi_string_list_add_many_va(r, va);
+ va_end(va);
+
+ return r;
+}
+
+AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va) {
+ const gchar *txt;
+
+ while ((txt = va_arg(va, const gchar*)))
+ r = avahi_string_list_add(r, txt);
+
+ return r;
+}
+
+
+AvahiStringList *avahi_string_list_new(const gchar *txt, ...) {
+ va_list va;
+ AvahiStringList *r = NULL;
+
+ if (txt) {
+ r = avahi_string_list_add(r, txt);
+
+ va_start(va, txt);
+ r = avahi_string_list_add_many_va(r, va);
+ va_end(va);
+ }
+
+ return r;
+}
+
+AvahiStringList *avahi_string_list_new_va(va_list va) {
+ return avahi_string_list_add_many_va(NULL, va);
+}
+
+AvahiStringList *avahi_string_list_copy(AvahiStringList *l) {
+ AvahiStringList *r = NULL;
+
+ for (; l; l = l->next)
+ r = avahi_string_list_add_arbitrary(r, l->text, l->size);
+
+ return string_list_reverse(r);
+}
diff --git a/avahi-core/strlst.h b/avahi-core/strlst.h
new file mode 100644
index 0000000..42377c0
--- /dev/null
+++ b/avahi-core/strlst.h
@@ -0,0 +1,55 @@
+#ifndef footxtlisthfoo
+#define footxtlisthfoo
+
+/* $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 <glib.h>
+
+typedef struct _AvahiStringList AvahiStringList;
+
+struct _AvahiStringList {
+ AvahiStringList *next;
+ guint size;
+ guint8 text[1];
+};
+
+AvahiStringList *avahi_string_list_new(const gchar *txt, ...);
+AvahiStringList *avahi_string_list_new_va(va_list va);
+
+void avahi_string_list_free(AvahiStringList *l);
+
+AvahiStringList *avahi_string_list_add(AvahiStringList *l, const gchar *text);
+AvahiStringList *avahi_string_list_add_arbitrary(AvahiStringList *l, const guint8 *text, guint size);
+AvahiStringList *avahi_string_list_add_many(AvahiStringList *r, ...);
+AvahiStringList *avahi_string_list_add_many_va(AvahiStringList *r, va_list va);
+
+gchar* avahi_string_list_to_string(AvahiStringList *l);
+
+guint avahi_string_list_serialize(AvahiStringList *l, gpointer data, guint size);
+AvahiStringList *avahi_string_list_parse(gconstpointer data, guint size);
+
+gboolean avahi_string_list_equal(AvahiStringList *a, AvahiStringList *b);
+
+AvahiStringList *avahi_string_list_copy(AvahiStringList *l);
+
+#endif
+
diff --git a/avahi-core/subscribe.c b/avahi-core/subscribe.c
new file mode 100644
index 0000000..00d3385
--- /dev/null
+++ b/avahi-core/subscribe.c
@@ -0,0 +1,158 @@
+/* $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 "subscribe.h"
+#include "util.h"
+
+static void elapse(AvahiTimeEvent *e, void *userdata) {
+ AvahiSubscription *s = userdata;
+ GTimeVal tv;
+ gchar *t;
+
+ g_assert(s);
+
+ avahi_server_post_query(s->server, s->interface, s->protocol, s->key);
+
+ if (s->n_query++ <= 8)
+ s->sec_delay *= 2;
+
+ g_message("%i. Continuous querying for %s", s->n_query, t = avahi_key_to_string(s->key));
+ g_free(t);
+
+ avahi_elapse_time(&tv, s->sec_delay*1000, 0);
+ avahi_time_event_queue_update(s->server->time_event_queue, s->time_event, &tv);
+}
+
+struct cbdata {
+ AvahiSubscription *subscription;
+ AvahiInterface *interface;
+};
+
+static gpointer scan_cache_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
+ struct cbdata *cbdata = userdata;
+
+ g_assert(c);
+ g_assert(pattern);
+ g_assert(e);
+ g_assert(cbdata);
+
+ cbdata->subscription->callback(
+ cbdata->subscription,
+ e->record,
+ cbdata->interface->hardware->index,
+ cbdata->interface->protocol,
+ AVAHI_SUBSCRIPTION_NEW,
+ cbdata->subscription->userdata);
+
+ return NULL;
+}
+
+static void scan_interface_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, gpointer userdata) {
+ AvahiSubscription *s = userdata;
+ struct cbdata cbdata = { s, i };
+
+ g_assert(m);
+ g_assert(i);
+ g_assert(s);
+
+ avahi_cache_walk(i->cache, s->key, scan_cache_callback, &cbdata);
+}
+
+AvahiSubscription *avahi_subscription_new(AvahiServer *server, AvahiKey *key, gint interface, guchar protocol, AvahiSubscriptionCallback callback, gpointer userdata) {
+ AvahiSubscription *s, *t;
+ GTimeVal tv;
+
+ g_assert(server);
+ g_assert(key);
+ g_assert(callback);
+
+ g_assert(!avahi_key_is_pattern(key));
+
+ s = g_new(AvahiSubscription, 1);
+ s->server = server;
+ s->key = avahi_key_ref(key);
+ s->interface = interface;
+ s->protocol = protocol;
+ s->callback = callback;
+ s->userdata = userdata;
+ s->n_query = 1;
+ s->sec_delay = 1;
+
+ avahi_server_post_query(s->server, s->interface, s->protocol, s->key);
+
+ avahi_elapse_time(&tv, s->sec_delay*1000, 0);
+ s->time_event = avahi_time_event_queue_add(server->time_event_queue, &tv, elapse, s);
+
+ AVAHI_LLIST_PREPEND(AvahiSubscription, subscriptions, server->subscriptions, s);
+
+ /* Add the new entry to the subscription hash table */
+ t = g_hash_table_lookup(server->subscription_hashtable, key);
+ AVAHI_LLIST_PREPEND(AvahiSubscription, by_key, t, s);
+ g_hash_table_replace(server->subscription_hashtable, key, t);
+
+ /* Scan the caches */
+ avahi_interface_monitor_walk(s->server->monitor, s->interface, s->protocol, scan_interface_callback, s);
+
+ return s;
+}
+
+void avahi_subscription_free(AvahiSubscription *s) {
+ AvahiSubscription *t;
+
+ g_assert(s);
+
+ AVAHI_LLIST_REMOVE(AvahiSubscription, subscriptions, s->server->subscriptions, s);
+
+ t = g_hash_table_lookup(s->server->subscription_hashtable, s->key);
+ AVAHI_LLIST_REMOVE(AvahiSubscription, by_key, t, s);
+ if (t)
+ g_hash_table_replace(s->server->subscription_hashtable, t->key, t);
+ else
+ g_hash_table_remove(s->server->subscription_hashtable, s->key);
+
+ avahi_time_event_queue_remove(s->server->time_event_queue, s->time_event);
+ avahi_key_unref(s->key);
+
+
+ g_free(s);
+}
+
+void avahi_subscription_notify(AvahiServer *server, AvahiInterface *i, AvahiRecord *record, AvahiSubscriptionEvent event) {
+ AvahiSubscription *s;
+
+ g_assert(server);
+ g_assert(record);
+
+ for (s = g_hash_table_lookup(server->subscription_hashtable, record->key); s; s = s->by_key_next)
+ if (avahi_interface_match(i, s->interface, s->protocol))
+ s->callback(s, record, i->hardware->index, i->protocol, event, s->userdata);
+}
+
+gboolean avahi_is_subscribed(AvahiServer *server, AvahiKey *k) {
+ g_assert(server);
+ g_assert(k);
+
+ return !!g_hash_table_lookup(server->subscription_hashtable, k);
+}
diff --git a/avahi-core/subscribe.h b/avahi-core/subscribe.h
new file mode 100644
index 0000000..307393e
--- /dev/null
+++ b/avahi-core/subscribe.h
@@ -0,0 +1,52 @@
+#ifndef foosubscribehfoo
+#define foosubscribehfoo
+
+/* $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 "llist.h"
+#include "avahi.h"
+#include "subscribe.h"
+#include "timeeventq.h"
+#include "server.h"
+
+struct _AvahiSubscription {
+ AvahiServer *server;
+ AvahiKey *key;
+ gint interface;
+ guchar protocol;
+ gint n_query;
+ guint sec_delay;
+
+ AvahiTimeEvent *time_event;
+
+ AvahiSubscriptionCallback callback;
+ gpointer userdata;
+
+ AVAHI_LLIST_FIELDS(AvahiSubscription, subscriptions);
+ AVAHI_LLIST_FIELDS(AvahiSubscription, by_key);
+};
+
+void avahi_subscription_notify(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, AvahiSubscriptionEvent event);
+
+gboolean avahi_is_subscribed(AvahiServer *s, AvahiKey *k);
+
+#endif
diff --git a/avahi-core/timeeventq.c b/avahi-core/timeeventq.c
new file mode 100644
index 0000000..efce7b7
--- /dev/null
+++ b/avahi-core/timeeventq.c
@@ -0,0 +1,185 @@
+/* $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 "timeeventq.h"
+#include "util.h"
+
+static gint compare(gconstpointer _a, gconstpointer _b) {
+ const AvahiTimeEvent *a = _a, *b = _b;
+
+ return avahi_timeval_compare(&a->expiry, &b->expiry);
+}
+
+static gboolean prepare_func(GSource *source, gint *timeout) {
+ AvahiTimeEventQueue *q = (AvahiTimeEventQueue*) source;
+ AvahiTimeEvent *e;
+ GTimeVal now;
+
+ g_assert(source);
+ g_assert(timeout);
+
+ if (!q->prioq->root) {
+ *timeout = -1;
+ return FALSE;
+ }
+
+ e = q->prioq->root->data;
+ g_assert(e);
+
+ g_source_get_current_time(source, &now);
+
+ if (avahi_timeval_compare(&now, &e->expiry) >= 0) {
+ *timeout = -1;
+ return TRUE;
+ }
+
+ *timeout = (gint) (avahi_timeval_diff(&e->expiry, &now)/1000);
+
+ return FALSE;
+}
+
+static gboolean check_func(GSource *source) {
+ AvahiTimeEventQueue *q = (AvahiTimeEventQueue*) source;
+ AvahiTimeEvent *e;
+ GTimeVal now;
+
+ g_assert(source);
+
+ if (!q->prioq->root)
+ return FALSE;
+
+ e = q->prioq->root->data;
+ g_assert(e);
+
+ g_source_get_current_time(source, &now);
+
+ return avahi_timeval_compare(&now, &e->expiry) >= 0;
+}
+
+static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
+ AvahiTimeEventQueue *q = (AvahiTimeEventQueue*) source;
+ GTimeVal now;
+
+ g_assert(source);
+
+ g_source_get_current_time(source, &now);
+
+ while (q->prioq->root) {
+ AvahiTimeEvent *e = q->prioq->root->data;
+
+ if (avahi_timeval_compare(&now, &e->expiry) < 0)
+ break;
+
+ g_assert(e->callback);
+ e->callback(e, e->userdata);
+ }
+
+ return TRUE;
+}
+
+AvahiTimeEventQueue* avahi_time_event_queue_new(GMainContext *context, gint priority) {
+ AvahiTimeEventQueue *q;
+
+ static GSourceFuncs source_funcs = {
+ prepare_func,
+ check_func,
+ dispatch_func,
+ NULL,
+ NULL,
+ NULL
+ };
+
+ q = (AvahiTimeEventQueue*) g_source_new(&source_funcs, sizeof(AvahiTimeEventQueue));
+ q->prioq = avahi_prio_queue_new(compare);
+
+ g_source_set_priority((GSource*) q, priority);
+
+ g_source_attach(&q->source, context);
+
+ return q;
+}
+
+void avahi_time_event_queue_free(AvahiTimeEventQueue *q) {
+ g_assert(q);
+
+ while (q->prioq->root)
+ avahi_time_event_queue_remove(q, q->prioq->root->data);
+ avahi_prio_queue_free(q->prioq);
+
+ g_source_destroy(&q->source);
+ g_source_unref(&q->source);
+}
+
+AvahiTimeEvent* avahi_time_event_queue_add(AvahiTimeEventQueue *q, const GTimeVal *timeval, void (*callback)(AvahiTimeEvent *e, void *userdata), void *userdata) {
+ AvahiTimeEvent *e;
+
+ g_assert(q);
+ g_assert(timeval);
+ g_assert(callback);
+ g_assert(userdata);
+
+ e = g_new(AvahiTimeEvent, 1);
+ e->queue = q;
+ e->expiry = *timeval;
+ e->callback = callback;
+ e->userdata = userdata;
+
+ e->node = avahi_prio_queue_put(q->prioq, e);
+
+ return e;
+}
+
+void avahi_time_event_queue_remove(AvahiTimeEventQueue *q, AvahiTimeEvent *e) {
+ g_assert(q);
+ g_assert(e);
+ g_assert(e->queue == q);
+
+ avahi_prio_queue_remove(q->prioq, e->node);
+ g_free(e);
+}
+
+void avahi_time_event_queue_update(AvahiTimeEventQueue *q, AvahiTimeEvent *e, const GTimeVal *timeval) {
+ g_assert(q);
+ g_assert(e);
+ g_assert(e->queue == q);
+
+ e->expiry = *timeval;
+
+ avahi_prio_queue_shuffle(q->prioq, e->node);
+}
+
+AvahiTimeEvent* avahi_time_event_queue_root(AvahiTimeEventQueue *q) {
+ g_assert(q);
+
+ return q->prioq->root ? q->prioq->root->data : NULL;
+}
+
+AvahiTimeEvent* avahi_time_event_next(AvahiTimeEvent *e) {
+ g_assert(e);
+
+ return e->node->next->data;
+}
+
+
diff --git a/avahi-core/timeeventq.h b/avahi-core/timeeventq.h
new file mode 100644
index 0000000..26ced90
--- /dev/null
+++ b/avahi-core/timeeventq.h
@@ -0,0 +1,57 @@
+#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.
+***/
+
+typedef struct _AvahiTimeEventQueue AvahiTimeEventQueue;
+typedef struct _AvahiTimeEvent AvahiTimeEvent;
+
+#include "prioq.h"
+
+struct _AvahiTimeEvent {
+ AvahiTimeEventQueue *queue;
+ AvahiPrioQueueNode *node;
+ GTimeVal expiry;
+ void (*callback)(AvahiTimeEvent *e, void *userdata);
+ void *userdata;
+};
+
+struct _AvahiTimeEventQueue {
+ GSource source;
+ AvahiPrioQueue *prioq;
+};
+
+AvahiTimeEventQueue* avahi_time_event_queue_new(GMainContext *context, gint priority);
+void avahi_time_event_queue_free(AvahiTimeEventQueue *q);
+
+AvahiTimeEvent* avahi_time_event_queue_add(AvahiTimeEventQueue *q, const GTimeVal *timeval, void (*callback)(AvahiTimeEvent *e, void *userdata), void *userdata);
+void avahi_time_event_queue_remove(AvahiTimeEventQueue *q, AvahiTimeEvent *e);
+
+void avahi_time_event_queue_update(AvahiTimeEventQueue *q, AvahiTimeEvent *e, const GTimeVal *timeval);
+
+AvahiTimeEvent* avahi_time_event_queue_root(AvahiTimeEventQueue *q);
+AvahiTimeEvent* avahi_time_event_next(AvahiTimeEvent *e);
+
+
+
+
+#endif
diff --git a/avahi-core/util.c b/avahi-core/util.c
new file mode 100644
index 0000000..fa97eec
--- /dev/null
+++ b/avahi-core/util.c
@@ -0,0 +1,239 @@
+/* $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 <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#include "util.h"
+
+gchar *avahi_get_host_name(void) {
+#ifdef HOST_NAME_MAX
+ char t[HOST_NAME_MAX];
+#else
+ char t[256];
+#endif
+ gethostname(t, sizeof(t));
+ t[sizeof(t)-1] = 0;
+ return avahi_normalize_name(t);
+}
+
+gchar *avahi_normalize_name(const gchar *s) {
+ size_t l;
+ g_assert(s);
+
+ l = strlen(s);
+
+ if (!l)
+ return g_strdup(".");
+
+ if (s[l-1] == '.')
+ return g_strdup(s);
+
+ return g_strdup_printf("%s.", s);
+}
+
+gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b) {
+ g_assert(a);
+ g_assert(b);
+
+ if (a->tv_sec < b->tv_sec)
+ return -1;
+
+ if (a->tv_sec > b->tv_sec)
+ return 1;
+
+ if (a->tv_usec < b->tv_usec)
+ return -1;
+
+ if (a->tv_usec > b->tv_usec)
+ return 1;
+
+ return 0;
+}
+
+glong avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b) {
+ g_assert(a);
+ g_assert(b);
+
+ if (avahi_timeval_compare(a, b) < 0)
+ return avahi_timeval_diff(b, a);
+
+ return ((glong) a->tv_sec - b->tv_sec)*1000000 + a->tv_usec - b->tv_usec;
+}
+
+
+gint avahi_set_cloexec(gint fd) {
+ gint n;
+
+ g_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);
+}
+
+gint avahi_set_nonblock(gint fd) {
+ gint n;
+
+ g_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);
+}
+
+gint avahi_wait_for_write(gint fd) {
+ fd_set fds;
+ gint r;
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+
+ if ((r = select(fd+1, NULL, &fds, NULL, NULL)) < 0) {
+ g_message("select() failed: %s", strerror(errno));
+
+ return -1;
+ }
+
+ g_assert(r > 0);
+
+ return 0;
+}
+
+GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter) {
+ g_assert(tv);
+
+ g_get_current_time(tv);
+
+ if (msec)
+ g_time_val_add(tv, msec*1000);
+
+ if (jitter)
+ g_time_val_add(tv, g_random_int_range(0, jitter) * 1000);
+
+ return tv;
+}
+
+gint avahi_age(const GTimeVal *a) {
+ GTimeVal now;
+
+ g_assert(a);
+
+ g_get_current_time(&now);
+
+ return avahi_timeval_diff(&now, a);
+}
+
+gboolean avahi_domain_cmp(const gchar *a, const gchar *b) {
+ int escaped_a = 0, escaped_b = 0;
+ g_assert(a);
+ g_assert(b);
+
+ for (;;) {
+ /* Check for escape characters "\" */
+ if ((escaped_a = *a == '\\'))
+ a ++;
+
+ if ((escaped_b = *b == '\\'))
+ b++;
+
+ /* Check for string end */
+ if (*a == 0 && *b == 0)
+ return 0;
+
+ if (*a == 0 && !escaped_b && *b == '.' && *(b+1) == 0)
+ return 0;
+
+ if (!escaped_a && *a == '.' && *(a+1) == 0 && *b == 0)
+ return 0;
+
+ /* Compare characters */
+ if (escaped_a == escaped_b && *a != *b)
+ return *a < *b ? -1 : 1;
+
+ /* Next characters */
+ a++;
+ b++;
+
+ }
+}
+
+gboolean avahi_domain_equal(const gchar *a, const gchar *b) {
+ return avahi_domain_cmp(a, b) == 0;
+}
+
+guint avahi_domain_hash(const gchar *p) {
+ char t[256];
+ strncpy(t, p, sizeof(t)-1);
+ t[sizeof(t)-1] = 0;
+
+ return g_int_hash(t);
+}
+
+void avahi_hexdump(gconstpointer p, guint size) {
+ const guint8 *c = p;
+ g_assert(p);
+
+ printf("Dumping %u bytes from %p:\n", size, p);
+
+ while (size > 0) {
+ guint 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;
+ }
+}
diff --git a/avahi-core/util.h b/avahi-core/util.h
new file mode 100644
index 0000000..1186cc2
--- /dev/null
+++ b/avahi-core/util.h
@@ -0,0 +1,47 @@
+#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 <glib.h>
+
+gchar *avahi_normalize_name(const gchar *s); /* g_free() the result! */
+gchar *avahi_get_host_name(void); /* g_free() the result! */
+
+gint avahi_timeval_compare(const GTimeVal *a, const GTimeVal *b);
+glong avahi_timeval_diff(const GTimeVal *a, const GTimeVal *b);
+
+gint avahi_set_cloexec(gint fd);
+gint avahi_set_nonblock(gint fd);
+gint avahi_wait_for_write(gint fd);
+
+GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter);
+
+gint avahi_age(const GTimeVal *a);
+
+guint avahi_domain_hash(const gchar *p);
+gboolean avahi_domain_cmp(const gchar *a, const gchar *b);
+gboolean avahi_domain_equal(const gchar *a, const gchar *b);
+
+void avahi_hexdump(gconstpointer p, guint size);
+
+#endif