From f5a4db2039532ef93fbb3d98fa048be9d74a83fe Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 16 May 2005 01:01:43 +0000 Subject: * case insensitive name comparisons * corectly handle RRsets * make addresses unique * return to probe state on conflict * a bunch of fixes detected while testing against the Apple Bonjour Conformance Test * a fix in avahi_record_lexicographical_cmp() regarding TXT records * API for choosing alternative host and service names * remove a bunch of unused crap from cache.c and server.c * flush cache when an interface becomes unavailable git-svn-id: file:///home/lennart/svn/public/avahi/trunk@72 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-core/Makefile.am | 17 ++- avahi-core/alternative-test.c | 48 ++++++++ avahi-core/announce.c | 107 ++++++++++------ avahi-core/announce.h | 4 +- avahi-core/avahi-test.c | 24 ++-- avahi-core/cache.c | 125 +++++++++---------- avahi-core/cache.h | 4 +- avahi-core/core.h | 4 +- avahi-core/iface.c | 11 +- avahi-core/iface.h | 2 +- avahi-core/psched.c | 241 ++++++++++++++++++++---------------- avahi-core/psched.h | 9 +- avahi-core/rr.c | 12 +- avahi-core/server.c | 276 ++++++++++++++++++++++-------------------- avahi-core/server.h | 7 +- avahi-core/util.c | 54 +++++++++ avahi-core/util.h | 4 + todo | 11 +- 18 files changed, 585 insertions(+), 375 deletions(-) create mode 100644 avahi-core/alternative-test.c diff --git a/avahi-core/Makefile.am b/avahi-core/Makefile.am index e6708b6..cd22097 100644 --- a/avahi-core/Makefile.am +++ b/avahi-core/Makefile.am @@ -27,14 +27,15 @@ AM_LDADD=$(GLIB20_LIBS) AM_CFLAGS+='-DDEBUG_TRAP=__asm__("int $$3")' lib_LTLIBRARIES = \ - libavahi-core.la + libavahi-core.la noinst_PROGRAMS = \ dns-test \ domain-test \ prioq-test \ strlst-test \ - avahi-test + avahi-test \ + alternative-test libavahi_core_la_SOURCES = \ timeeventq.c timeeventq.h\ @@ -82,9 +83,17 @@ dns_test_CFLAGS = $(AM_CFLAGS) dns_test_LDADD = $(AM_LDADD) avahi_test_SOURCES = \ - avahi-test.c + avahi-test.c \ + $(libavahi_core_la_SOURCES) + avahi_test_CFLAGS = $(AM_CFLAGS) -avahi_test_LDADD = $(AM_LDADD) libavahi-core.la +avahi_test_LDADD = $(AM_LDADD) + +alternative_test_SOURCES = \ + alternative-test.c \ + util.c util.h +alternative_test_CFLAGS = $(AM_CFLAGS) +alternative_test_LDADD = $(AM_LDADD) valgrind: avahi-test libtool --mode=execute valgrind ./avahi-test diff --git a/avahi-core/alternative-test.c b/avahi-core/alternative-test.c new file mode 100644 index 0000000..18bc698 --- /dev/null +++ b/avahi-core/alternative-test.c @@ -0,0 +1,48 @@ +/* $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 +#endif + +#include + +#include "util.h" + +int main(int argc, char *argv[]) { + gchar *r = NULL; + gint i, k; + + for (k = 0; k < 2; k++) { + + for (i = 0; i < 20; i++) { + gchar *n; + + n = i == 0 ? g_strdup("gurke") : (k ? avahi_alternative_service_name(r) : avahi_alternative_host_name(r)); + g_free(r); + r = n; + + printf("%s\n", r); + } + } + + g_free(r); +} diff --git a/avahi-core/announce.c b/avahi-core/announce.c index 5ee0f48..8149b38 100644 --- a/avahi-core/announce.c +++ b/avahi-core/announce.c @@ -152,7 +152,13 @@ static void next_state(AvahiAnnouncement *a) { } else if (a->state == AVAHI_ANNOUNCING) { - avahi_interface_post_response(a->interface, a->entry->record, a->entry->flags & AVAHI_ENTRY_UNIQUE, FALSE); + if (a->entry->flags & AVAHI_ENTRY_UNIQUE) + /* Send the whole rrset at once */ + avahi_server_prepare_matching_responses(a->server, a->interface, a->entry->record->key, FALSE); + else + avahi_server_prepare_response(a->server, a->interface, a->entry, FALSE); + + avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, FALSE); if (++a->n_iteration >= 4) { gchar *t; @@ -196,9 +202,43 @@ AvahiAnnouncement *avahi_get_announcement(AvahiServer *s, AvahiEntry *e, AvahiIn return NULL; } +static void go_to_initial_state(AvahiAnnouncement *a, gboolean immediately) { + AvahiEntry *e; + GTimeVal tv; + + g_assert(a); + e = a->entry; + + 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; + + a->n_iteration = 1; + a->sec_delay = 1; + + if (a->state == AVAHI_PROBING && e->group) + e->group->n_probing++; + + if (a->state == AVAHI_PROBING) { + avahi_elapse_time(&tv, 0, immediately ? 0 : AVAHI_PROBE_JITTER_MSEC); + set_timeout(a, &tv); + } else if (a->state == AVAHI_ANNOUNCING) { + avahi_elapse_time(&tv, 0, immediately ? 0 : AVAHI_ANNOUNCEMENT_JITTER_MSEC); + set_timeout(a, &tv); + } else + set_timeout(a, NULL); +} + static void new_announcement(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) { AvahiAnnouncement *a; - GTimeVal tv; gchar *t; g_assert(s); @@ -220,41 +260,15 @@ static void new_announcement(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) { 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); - } + go_to_initial_state(a, FALSE); + + 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); } void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) { @@ -312,10 +326,13 @@ gboolean avahi_entry_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i if (!(a = avahi_get_announcement(s, e, i))) return FALSE; - return a->state == AVAHI_ANNOUNCING || a->state == AVAHI_ESTABLISHED; + return + a->state == AVAHI_ANNOUNCING || + a->state == AVAHI_ESTABLISHED || + (a->state == AVAHI_WAITING && !(e->flags & AVAHI_ENTRY_UNIQUE)); } -gboolean avahi_entry_registering(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { +gboolean avahi_entry_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { AvahiAnnouncement *a; g_assert(s); @@ -326,7 +343,25 @@ gboolean avahi_entry_registering(AvahiServer *s, AvahiEntry *e, AvahiInterface * if (!(a = avahi_get_announcement(s, e, i))) return FALSE; - return a->state == AVAHI_PROBING || a->state == AVAHI_WAITING; + return + a->state == AVAHI_PROBING || + (a->state == AVAHI_WAITING && (e->flags & AVAHI_ENTRY_UNIQUE)); +} + +void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) { + AvahiAnnouncement *a; + + g_assert(s); + g_assert(e); + g_assert(i); + + if (!(a = avahi_get_announcement(s, e, i))) + return; + + if (a->state == AVAHI_PROBING && a->entry->group) + a->entry->group->n_probing--; + + go_to_initial_state(a, TRUE); } static AvahiRecord *make_goodbye_record(AvahiRecord *r) { @@ -364,7 +399,7 @@ static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, g return; g = make_goodbye_record(e->record); - avahi_interface_post_response(i, g, e->flags & AVAHI_ENTRY_UNIQUE, TRUE); + avahi_interface_post_response(i, g, e->flags & AVAHI_ENTRY_UNIQUE, TRUE, NULL); avahi_record_unref(g); } diff --git a/avahi-core/announce.h b/avahi-core/announce.h index 389c7ca..244a00f 100644 --- a/avahi-core/announce.h +++ b/avahi-core/announce.h @@ -57,10 +57,12 @@ 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_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i); + 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); +gboolean avahi_entry_probing(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); diff --git a/avahi-core/avahi-test.c b/avahi-core/avahi-test.c index 553a021..e275649 100644 --- a/avahi-core/avahi-test.c +++ b/avahi-core/avahi-test.c @@ -50,7 +50,7 @@ static void subscription(AvahiSubscription *s, AvahiRecord *r, gint interface, g 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")); + event == AVAHI_SUBSCRIPTION_NEW ? "new" : "removed"); g_free(t); } @@ -61,7 +61,6 @@ static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryG int main(int argc, char *argv[]) { AvahiServer *avahi; - gchar *r; GMainLoop *loop = NULL; AvahiSubscription *s; AvahiKey *k; @@ -69,32 +68,31 @@ int main(int argc, char *argv[]) { avahi = avahi_server_new(NULL); - g = avahi_entry_group_new(avahi, entry_group_callback, NULL); + g = avahi_entry_group_new(avahi, entry_group_callback, NULL); - avahi_server_add_text(avahi, g, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, "HALLO", "hallo", NULL); - avahi_server_add_text(avahi, g, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, "hallo", "waldo", NULL); +/* avahi_server_add_text(avahi, g, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, "HALLO", avahi_server_get_hostname(avahi), NULL); */ +/* avahi_server_add_text(avahi, g, 0, AF_UNSPEC, AVAHI_ENTRY_UNIQUE, "hallo", "waldo", NULL); */ - avahi_server_add_service(avahi, g, 0, AF_UNSPEC, "_http._tcp", "gurke", NULL, NULL, 80, "foo", NULL); + avahi_server_add_service(avahi, g, 0, AF_UNSPEC, "_http._tcp", "gurke", NULL, NULL, 80, "foo", NULL); - avahi_entry_group_commit(g); + avahi_entry_group_commit(g); avahi_server_dump(avahi, stdout); - -/* k = avahi_key_new("ecstasy.local.", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_ANY); */ +/* k = avahi_key_new("HALLO", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); */ /* 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_timeout_add(1000*5, 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_subscription_free(s); */ + avahi_entry_group_free(g); avahi_server_free(avahi); return 0; diff --git a/avahi-core/cache.c b/avahi-core/cache.c index 5b9c881..cba9c0b 100644 --- a/avahi-core/cache.c +++ b/avahi-core/cache.c @@ -220,8 +220,17 @@ static void next_expiry(AvahiCache *c, AvahiCacheEntry *e, guint percent) { update_time_event(c, e); } +static void expire_in_one_second(AvahiCache *c, AvahiCacheEntry *e) { + g_assert(c); + g_assert(e); + + e->state = AVAHI_CACHE_FINAL; + g_get_current_time(&e->expiry); + g_time_val_add(&e->expiry, 1000000); /* 1s */ + 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); @@ -231,51 +240,51 @@ void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const Av g_free(txt); if (r->ttl == 0) { - /* This is a goodbye request */ - if ((e = avahi_cache_lookup_record(c, r))) { + AvahiCacheEntry *e; - 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); - } + if ((e = avahi_cache_lookup_record(c, r))) + expire_in_one_second(c, e); } else { + AvahiCacheEntry *e = NULL, *first; + GTimeVal now; + + g_get_current_time(&now); /* This is an update request */ - if ((t = e = avahi_cache_lookup_key(c, r->key))) { - + if ((first = 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; + + /* For unique entries drop all entries older than one second */ + for (e = first; e; e = e->by_key_next) { + glong t; + + t = avahi_timeval_diff(&now, &e->timestamp); + + if (t > 1000000) + expire_in_one_second(c, e); + } } + + /* Look for exactly the same entry */ + for (e = first; e; e = e->by_key_next) + if (avahi_record_equal_no_ttl(e->record, r)) + break; } if (e) { -/* 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 */ + g_message("found matching cache entry"); + + /* We need to update the hash table key if we replace the + * record */ 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); @@ -283,7 +292,7 @@ void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const Av } else { /* No entry found, therefore we create a new one */ -/* g_message("couldn't find matching cache entry"); */ + g_message("couldn't find matching cache entry"); e = g_new(AvahiCacheEntry, 1); e->cache = c; @@ -291,8 +300,8 @@ void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const Av 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); + AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, first, e); + g_hash_table_replace(c->hash_table, e->record->key, first); /* Append to linked list */ AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e); @@ -302,49 +311,24 @@ void avahi_cache_update(AvahiCache *c, AvahiRecord *r, gboolean unique, const Av } e->origin = *a; - g_get_current_time(&e->timestamp); + e->timestamp = now; 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) { +static void dump_callback(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); + + for (; e; e = e->by_key_next) { + gchar *t = avahi_record_to_string(e->record); + fprintf((FILE*) userdata, "%s\n", t); + g_free(t); + } } void avahi_cache_dump(AvahiCache *c, FILE *f) { @@ -352,7 +336,7 @@ void avahi_cache_dump(AvahiCache *c, FILE *f) { g_assert(f); fprintf(f, ";;; CACHE DUMP FOLLOWS ;;;\n"); - g_hash_table_foreach(c->hash_table, func, f); + g_hash_table_foreach(c->hash_table, dump_callback, f); } gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) { @@ -366,7 +350,14 @@ gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e) { age = avahi_timeval_diff(&now, &e->timestamp)/1000000; - g_message("age: %u, ttl/2: %u", age, e->record->ttl); +/* g_message("age: %u, ttl/2: %u", age, e->record->ttl); */ return age >= e->record->ttl/2; } + +void avahi_cache_flush(AvahiCache *c) { + g_assert(c); + + while (c->entries) + remove_entry(c, c->entries); +} diff --git a/avahi-core/cache.h b/avahi-core/cache.h index 3f05b8e..4415c3c 100644 --- a/avahi-core/cache.h +++ b/avahi-core/cache.h @@ -74,8 +74,6 @@ 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); @@ -83,4 +81,6 @@ gpointer avahi_cache_walk(AvahiCache *c, AvahiKey *pattern, AvahiCacheWalkCallba gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e); +void avahi_cache_flush(AvahiCache *c); + #endif diff --git a/avahi-core/core.h b/avahi-core/core.h index 42e6ee0..98f36b2 100644 --- a/avahi-core/core.h +++ b/avahi-core/core.h @@ -51,6 +51,9 @@ typedef void (*AvahiEntryGroupCallback) (AvahiServer *s, AvahiEntryGroup *g, Ava AvahiServer *avahi_server_new(GMainContext *c); void avahi_server_free(AvahiServer* s); +const gchar* avahi_server_get_domain(AvahiServer *s); +const gchar* avahi_server_get_host_name(AvahiServer *s); + const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiEntryGroup *g, void **state); void avahi_server_dump(AvahiServer *s, FILE *f); @@ -151,7 +154,6 @@ void avahi_server_add_service_strlst( typedef enum { AVAHI_SUBSCRIPTION_NEW, AVAHI_SUBSCRIPTION_REMOVE, - AVAHI_SUBSCRIPTION_CHANGE } AvahiSubscriptionEvent; typedef struct AvahiSubscription AvahiSubscription; diff --git a/avahi-core/iface.c b/avahi-core/iface.c index 23507f2..7f51305 100644 --- a/avahi-core/iface.c +++ b/avahi-core/iface.c @@ -49,7 +49,7 @@ static void update_address_rr(AvahiInterfaceMonitor *m, AvahiInterfaceAddress *a } 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_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); } } @@ -202,13 +202,14 @@ static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *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); + avahi_goodbye_interface(m->server, i, FALSE); + avahi_cache_flush(i->cache); + i->announcing = FALSE; } } @@ -510,12 +511,12 @@ gboolean avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, gboolean i return FALSE; } -gboolean avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, gboolean flush_cache, gboolean immediately) { +gboolean avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, gboolean flush_cache, gboolean immediately, const AvahiAddress *querier) { g_assert(i); g_assert(record); if (avahi_interface_relevant(i)) - return avahi_packet_scheduler_post_response(i->scheduler, record, flush_cache, immediately); + return avahi_packet_scheduler_post_response(i->scheduler, record, flush_cache, immediately, querier); return FALSE; } diff --git a/avahi-core/iface.h b/avahi-core/iface.h index 00aea9a..e229b2c 100644 --- a/avahi-core/iface.h +++ b/avahi-core/iface.h @@ -108,7 +108,7 @@ void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, c gboolean avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, gboolean immediately); gboolean avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, gboolean immediately); -gboolean avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, gboolean flush_cache, gboolean immediately); +gboolean avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, gboolean flush_cache, gboolean immediately, const AvahiAddress *querier); void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f); diff --git a/avahi-core/psched.c b/avahi-core/psched.c index 60bf40c..5121054 100644 --- a/avahi-core/psched.c +++ b/avahi-core/psched.c @@ -229,7 +229,7 @@ static void query_elapse(AvahiTimeEvent *e, gpointer data) { append_known_answers_and_send(s, p); } -AvahiQueryJob* query_job_new(AvahiPacketScheduler *s, AvahiKey *key) { +static AvahiQueryJob* query_job_new(AvahiPacketScheduler *s, AvahiKey *key) { AvahiQueryJob *qj; g_assert(s); @@ -246,6 +246,19 @@ AvahiQueryJob* query_job_new(AvahiPacketScheduler *s, AvahiKey *key) { return qj; } +static AvahiQueryJob* look_for_query(AvahiPacketScheduler *s, AvahiKey *key) { + AvahiQueryJob *qj; + + g_assert(s); + g_assert(key); + + for (qj = s->query_jobs; qj; qj = qj->jobs_next) + if (avahi_key_equal(qj->key, key)) + return qj; + + return NULL; +} + gboolean avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *key, gboolean immediately) { GTimeVal tv; AvahiQueryJob *qj; @@ -255,22 +268,23 @@ gboolean avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *ke 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)) { + if ((qj = look_for_query(s, key))) { + glong d = avahi_timeval_diff(&tv, &qj->delivery); - 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 FALSE; - } + /* Duplicate questions suppression */ + if (!qj->done || d <= AVAHI_QUERY_HISTORY_MSEC*1000) { + g_message("WARNING! DUPLICATE QUERY SUPPRESSION ACTIVE!"); + if (!qj->done && d < 0) { + /* If the new entry should be scheduled earlier, + * update the old entry */ + qj->delivery = tv; + avahi_time_event_queue_update(s->server->time_event_queue, qj->time_event, &qj->delivery); + } + + return FALSE; + } else query_job_free(s, qj); - break; - } - } qj = query_job_new(s, key); @@ -279,6 +293,34 @@ gboolean avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *ke return TRUE; } + +void avahi_packet_scheduler_incoming_query(AvahiPacketScheduler *s, AvahiKey *key) { + AvahiQueryJob *qj; + GTimeVal tv; + + g_assert(s); + g_assert(key); + + /* This function is called whenever an incoming query was + * receieved. We mark all matching queries that match as done. The + * keyword is "DUPLICATE QUESTION SUPPRESION". */ + + if (!(qj = look_for_query(s, key))) + qj = query_job_new(s, key); + + qj->done = TRUE; + + /* Drop the query after some time */ + avahi_elapse_time(&tv, AVAHI_QUERY_HISTORY_MSEC, 0); + + if (qj->time_event) + avahi_time_event_queue_update(s->server->time_event_queue, qj->time_event, &tv); + else + qj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &tv, query_elapse, qj); + + g_get_current_time(&qj->delivery); +} + static guint8* packet_add_response_job(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) { guint8 *d; @@ -376,13 +418,14 @@ static AvahiResponseJob* response_job_new(AvahiPacketScheduler *s, AvahiRecord * rj->done = FALSE; rj->time_event = NULL; rj->flush_cache = FALSE; + rj->querier_valid = FALSE; AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->response_jobs, rj); return rj; } -gboolean avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, AvahiRecord *record, gboolean flush_cache, gboolean immediately) { +gboolean avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, AvahiRecord *record, gboolean flush_cache, gboolean immediately, const AvahiAddress *querier) { AvahiResponseJob *rj; GTimeVal tv; @@ -396,70 +439,48 @@ gboolean avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, AvahiReco /* Don't send out duplicates */ if ((rj = look_for_response(s, record))) { - glong d; - - d = avahi_timeval_diff(&tv, &rj->delivery); + glong 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!"); - - rj->flush_cache = flush_cache; + + if ((!!record->ttl == !!rj->record->ttl) && + (rj->flush_cache || !flush_cache) && + ((!rj->done && d >= 0) || (rj->done && d <= AVAHI_RESPONSE_HISTORY_MSEC*1000))) { + g_message("Duplicate suppresion active."); return FALSE; } - /* Either one was a goodbye packet, but the other was not, so - * let's drop the older one. */ + /* If the old job was not yet done but scheduled earlier than + * our new one, we chedule our new job at the same time. */ + if (!rj->done && d > 0) + tv = rj->delivery; + + /* If the old job had the flush_cache bit enabled, we must + enable it on our new one, too */ + if (!rj->done && rj->flush_cache) + flush_cache = TRUE; + + /* For known answer suppresion we have record for which host this data was intended */ + if (querier && !rj->done && (!rj->querier_valid || avahi_address_cmp(&rj->querier, querier) != 0)) + querier = NULL; + + /* The old job wasn't good enough, so let's drop it */ 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); - return TRUE; -} - -void avahi_packet_scheduler_incoming_query(AvahiPacketScheduler *s, AvahiKey *key) { - GTimeVal tv; - AvahiQueryJob *qj; - - g_assert(s); - g_assert(key); + if ((rj->querier_valid = !!querier)) + rj->querier = *querier; - /* 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); + return TRUE; } void response_job_set_elapse_time(AvahiPacketScheduler *s, AvahiResponseJob *rj, guint msec, guint jitter) { @@ -476,8 +497,8 @@ void response_job_set_elapse_time(AvahiPacketScheduler *s, AvahiResponseJob *rj, 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; +void avahi_packet_scheduler_incoming_response(AvahiPacketScheduler *s, AvahiRecord *record, gboolean flush_cache) { + AvahiResponseJob *rj = NULL; g_assert(s); g_assert(record); @@ -485,51 +506,39 @@ void avahi_packet_scheduler_incoming_response(AvahiPacketScheduler *s, AvahiReco /* 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 { + if ((rj = look_for_response(s, record))) { + + if (!rj->done) { - /* Either one was a goodbye packet but other was - * none. We ignore the incoming packet. */ + if (rj->flush_cache && !flush_cache) + /* The incoming response didn't have flush_cache + * set, but our scheduled has => we still have to + * send our response */ + return; - return; - } + + if (!!record->ttl != !!rj->record->ttl) { + /* Either one was a goodbye packet but other was + * none => we still have to send our response */ + return; } } + + /* The two responses match, so let's mark the history + * entry as done or update it */ + } /* No matching job was found. Add the query to the history */ - rj = response_job_new(s, record); - -mark_done: + if (!rj) + rj = response_job_new(s, record); + else { + avahi_record_unref(rj->record); + rj->record = avahi_record_ref(record); + } + rj->done = TRUE; + rj->flush_cache = rj->flush_cache || flush_cache; /* Drop response after 500ms from history */ response_job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); @@ -537,6 +546,34 @@ mark_done: g_get_current_time(&rj->delivery); } + +void avahi_packet_scheduler_incoming_known_answer(AvahiPacketScheduler *s, AvahiRecord *record, const AvahiAddress *querier) { + AvahiResponseJob *rj; + + g_assert(s); + g_assert(record); + g_assert(querier); + + /* Check whether a matching job has been scheduled */ + if (!(rj = look_for_response(s, record)) || rj->done) + return; + + /* Chech whether another querier demanded the original job */ + if (!rj->querier_valid || avahi_address_cmp(&rj->querier, querier) != 0) + return; + + /* Check whether one of them is a goodbye packet, while the other is not */ + if (!!record->ttl != !!rj->record->ttl) + return; + + /* Check whether the known answer has a good TTL */ + if (record->ttl <= rj->record->ttl/2) + return; + + g_message("Known answer suppression active!"); + response_job_free(s, rj); +} + void avahi_packet_scheduler_flush_responses(AvahiPacketScheduler *s) { AvahiResponseJob *rj; @@ -660,7 +697,7 @@ static void probe_elapse(AvahiTimeEvent *e, gpointer data) { if (!pj->chosen) continue; - if (!avahi_dns_packet_append_record(p, pj->record, TRUE, 0)) { + if (!avahi_dns_packet_append_record(p, pj->record, FALSE, 0)) { g_warning("Bad probe size estimate!"); /* Unmark all following jobs */ diff --git a/avahi-core/psched.h b/avahi-core/psched.h index 12ea880..4b9e5b2 100644 --- a/avahi-core/psched.h +++ b/avahi-core/psched.h @@ -49,6 +49,10 @@ struct AvahiResponseJob { gboolean done; GTimeVal delivery; gboolean flush_cache; + + AvahiAddress querier; + gboolean querier_valid; + AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs); }; @@ -85,11 +89,12 @@ AvahiPacketScheduler *avahi_packet_scheduler_new(AvahiServer *server, AvahiInter void avahi_packet_scheduler_free(AvahiPacketScheduler *s); gboolean avahi_packet_scheduler_post_query(AvahiPacketScheduler *s, AvahiKey *key, gboolean immediately); -gboolean avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, AvahiRecord *record, gboolean flush_cache, gboolean immediately); +gboolean avahi_packet_scheduler_post_response(AvahiPacketScheduler *s, AvahiRecord *record, gboolean flush_cache, gboolean immediately, const AvahiAddress *querier); gboolean 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_response(AvahiPacketScheduler *s, AvahiRecord *record, gboolean flush_cache); +void avahi_packet_scheduler_incoming_known_answer(AvahiPacketScheduler *s, AvahiRecord *record, const AvahiAddress *querier); void avahi_packet_scheduler_flush_responses(AvahiPacketScheduler *s); diff --git a/avahi-core/rr.c b/avahi-core/rr.c index a9f6361..5b5ec7e 100644 --- a/avahi-core/rr.c +++ b/avahi-core/rr.c @@ -456,9 +456,17 @@ static gint uint16_cmp(guint16 a, guint16 b) { } gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { + gint r; +/* gchar *t1, *t2; */ + g_assert(a); g_assert(b); - gint r; + +/* t1 = avahi_record_to_string(a); */ +/* t2 = avahi_record_to_string(b); */ +/* g_message("lexicocmp: %s %s", t1, t2); */ +/* g_free(t1); */ +/* g_free(t2); */ if (a == b) return 0; @@ -500,7 +508,7 @@ gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { 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); + avahi_string_list_serialize(b->data.txt.string_list, mb, bsize); r = lexicographical_memcmp(ma, asize, mb, bsize); g_free(ma); diff --git a/avahi-core/server.c b/avahi-core/server.c index d66388a..e80b0c4 100644 --- a/avahi-core/server.c +++ b/avahi-core/server.c @@ -102,26 +102,19 @@ static void cleanup_dead(AvahiServer *s) { } } -static void post_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response); - static void add_aux_records(AvahiServer *s, AvahiInterface *i, const gchar *name, guint16 type, gboolean unicast_response) { AvahiKey *k; - AvahiEntry *e; - + g_assert(s); g_assert(i); g_assert(name); k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type); - - 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)) - post_response(s, i, e, unicast_response); - + avahi_server_prepare_matching_responses(s, i, k, unicast_response); avahi_key_unref(k); } -static void post_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response) { +void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response) { g_assert(s); g_assert(i); g_assert(e); @@ -140,7 +133,8 @@ static void post_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboo } } } -static void handle_query_key(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) { + +void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) { AvahiEntry *e; gchar *txt; @@ -148,18 +142,16 @@ static void handle_query_key(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gbo g_assert(i); g_assert(k); - g_message("Handling query: %s", txt = avahi_key_to_string(k)); + g_message("Posting responses matching [%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) { + if (avahi_key_is_pattern(k)) { /* 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)) - post_response(s, i, e, unicast_response); + avahi_server_prepare_response(s, i, e, unicast_response); } else { @@ -167,14 +159,13 @@ static void handle_query_key(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gbo 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)) - post_response(s, i, e, unicast_response); + avahi_server_prepare_response(s, i, e, unicast_response); } } static void withdraw_entry(AvahiServer *s, AvahiEntry *e) { g_assert(s); g_assert(e); - if (e->group) { AvahiEntry *k; @@ -193,33 +184,50 @@ static void withdraw_entry(AvahiServer *s, AvahiEntry *e) { s->need_entry_cleanup = TRUE; } +static void withdraw_rrset(AvahiServer *s, AvahiKey *key) { + AvahiEntry *e; + + g_assert(s); + g_assert(key); + + for (e = g_hash_table_lookup(s->entries_by_key, key); e; e = e->by_key_next) + withdraw_entry(s, e); +} + static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface *i) { AvahiEntry *e, *n; gchar *t; + gboolean ours = FALSE, won = FALSE, lost = FALSE; 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) { + gint cmp; n = e->by_key_next; - if (e->dead || avahi_record_equal_no_ttl(record, e->record)) + if (e->dead || !avahi_entry_probing(s, e, i)) continue; + + if ((cmp = avahi_record_lexicographical_compare(e->record, record)) == 0) { + ours = TRUE; + break; + } else if (cmp > 0) + won = TRUE; + else /* cmp < 0 */ + lost = TRUE; + } - if (avahi_entry_registering(s, e, i)) { - gint cmp; + t = avahi_record_to_string(record); - 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); + if (!ours) { + if (won) + g_message("Recieved conflicting probe [%s]. Local host won.", t); + else if (lost) { + g_message("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t); + withdraw_rrset(s, record->key); } } @@ -227,8 +235,8 @@ static void incoming_probe(AvahiServer *s, AvahiRecord *record, AvahiInterface * } static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord *record, gboolean unique, const AvahiAddress *a) { - gboolean valid = TRUE; - AvahiEntry *e, *n; + gboolean valid = TRUE, ours = FALSE, conflict = FALSE, withdraw_immediately = FALSE; + AvahiEntry *e, *n, *conflicting_entry = NULL; gchar *t; g_assert(s); @@ -237,68 +245,78 @@ static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord * t = avahi_record_to_string(record); -/* g_message("CHECKING FOR CONFLICT: [%s]", t); */ + 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) + if (e->dead || (!(e->flags & AVAHI_ENTRY_UNIQUE) && !unique)) continue; + + /* Either our entry or the other is intended to be unique, so let's check */ if (avahi_entry_registered(s, e, i)) { - gboolean equal = avahi_record_equal_no_ttl(record, e->record); + if (avahi_record_equal_no_ttl(e->record, record)) { + ours = TRUE; /* We have an identical record, so this is no conflict */ - /* 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); - + /* Check wheter there is a TTL conflict */ + if (record->ttl <= e->record->ttl/2) { + /* Refresh */ + g_message("Recieved record with bad TTL [%s]. Refreshing.", t); + avahi_interface_post_response(i, e->record, FALSE, TRUE, NULL); valid = FALSE; - avahi_interface_post_response(i, 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, e->record, e->flags & AVAHI_ENTRY_UNIQUE, TRUE); - g_message("Recieved record with bad TTL [%s]. Refreshing.", t); + /* There's no need to check the other entries of this RRset */ + break; + } else { + /* A conflict => we have to return to probe mode */ + conflict = TRUE; + conflicting_entry = e; } - - } else if (avahi_entry_registering(s, e, i)) { - if (!avahi_record_equal_no_ttl(record, e->record) && ((e->flags & AVAHI_ENTRY_UNIQUE) || unique)) { + } else if (avahi_entry_probing(s, e, i)) { + if (!avahi_record_equal_no_ttl(record, e->record)) { + /* 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); + conflict = TRUE; + withdraw_immediately = TRUE; } } } +/* g_message("ours=%i conflict=%i", ours, conflict); */ + + if (!ours && conflict) { + valid = FALSE; + + if (withdraw_immediately) { + g_message("Recieved conflicting record [%s] with local record to be. Withdrawing.", t); + withdraw_rrset(s, record->key); + } else { + g_assert(conflicting_entry); + g_message("Recieved conflicting record [%s]. Resetting our record.", t); + avahi_entry_return_to_initial_state(s, conflicting_entry, i); + + /* Local unique records are returned to probin + * state. Local shared records are reannounced. */ + } + } + g_free(t); return valid; } -static void generate_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) { +void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) { g_assert(s); - g_assert(p); g_assert(i); - g_assert(a); + g_assert(!legacy_unicast || (a && port > 0 && p)); if (legacy_unicast) { AvahiDnsPacket *reply; @@ -331,7 +349,7 @@ static void generate_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface while ((r = avahi_record_list_pop(s->record_list, &flush_cache, &unicast_response))) { - if (!avahi_interface_post_response(i, r, flush_cache, FALSE) && unicast_response) { + if (!avahi_interface_post_response(i, r, flush_cache, FALSE, a) && unicast_response) { /* Due to some reasons the record has not been scheduled. * The client requested an unicast response in that @@ -339,8 +357,10 @@ static void generate_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface for (;;) { - if (!reply) + if (!reply) { + g_assert(p); reply = avahi_dns_packet_new_reply(p, i->hardware->mtu, FALSE, FALSE); + } if (avahi_dns_packet_append_record(reply, r, flush_cache, 0)) { @@ -385,6 +405,8 @@ static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, c g_assert(i); g_assert(a); +/* g_message("query"); */ + g_assert(avahi_record_list_empty(s->record_list)); /* Handle the questions */ @@ -394,13 +416,18 @@ static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, c if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) { g_warning("Packet too short (1)"); - goto fail; - } + avahi_record_list_flush(s->record_list); + return; + } - handle_query_key(s, i, key, unicast_response); + avahi_packet_scheduler_incoming_query(i->scheduler, key); + avahi_server_prepare_matching_responses(s, i, key, unicast_response); avahi_key_unref(key); } + if (!avahi_record_list_empty(s->record_list)) + avahi_server_generate_response(s, i, p, a, port, legacy_unicast); + /* Known Answer Suppression */ for (n = avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ANCOUNT); n > 0; n --) { AvahiRecord *record; @@ -408,11 +435,11 @@ static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, c if (!(record = avahi_dns_packet_consume_record(p, &unique))) { g_warning("Packet too short (2)"); - goto fail; + return; } if (handle_conflict(s, i, record, unique, a)) - avahi_record_list_drop(s->record_list, record); + avahi_packet_scheduler_incoming_known_answer(i->scheduler, record, a); avahi_record_unref(record); } @@ -424,7 +451,7 @@ static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, c if (!(record = avahi_dns_packet_consume_record(p, &unique))) { g_warning("Packet too short (3)"); - goto fail; + return; } if (record->key->type != AVAHI_DNS_TYPE_ANY) @@ -432,14 +459,6 @@ static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, c avahi_record_unref(record); } - - if (!avahi_record_list_empty(s->record_list)) - generate_response(s, p, i, a, port, legacy_unicast); - - return; - -fail: - avahi_record_list_flush(s->record_list); } static void handle_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a) { @@ -449,6 +468,8 @@ static void handle_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i g_assert(p); g_assert(i); g_assert(a); + +/* g_message("response"); */ 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--) { @@ -468,7 +489,7 @@ static void handle_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i 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_packet_scheduler_incoming_response(i->scheduler, record, cache_flush); } } @@ -486,7 +507,8 @@ static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, struct sockaddr * g_assert(sa); g_assert(iface > 0); - if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family))) { + if (!(i = avahi_interface_monitor_get_interface(s->monitor, iface, sa->sa_family)) || + !avahi_interface_relevant(i)) { g_warning("Recieved packet from invalid interface."); return; } @@ -516,8 +538,7 @@ static void dispatch_packet(AvahiServer *s, AvahiDnsPacket *p, struct sockaddr * 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) { + if (avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_ARCOUNT) != 0) { g_warning("Invalid query packet."); return; } @@ -623,9 +644,11 @@ static void add_default_entries(AvahiServer *s) { AvahiRecord *r; g_assert(s); + + return ; /* Fill in HINFO rr */ - r = avahi_record_new_full(s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO); + r = avahi_record_new_full(s->host_name_fqdn, 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)); @@ -634,14 +657,13 @@ static void add_default_entries(AvahiServer *s) { /* 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_server_add_address(s, NULL, 0, AF_UNSPEC, 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); + avahi_server_add_address(s, NULL, 0, AF_UNSPEC, AVAHI_ENTRY_NOPROBE|AVAHI_ENTRY_NOANNOUNCE, "ip6-localhost", &a); } AvahiServer *avahi_server_new(GMainContext *c) { - gchar *hn; AvahiServer *s; static GSourceFuncs source_funcs = { @@ -659,7 +681,7 @@ AvahiServer *avahi_server_new(GMainContext *c) { s->need_entry_cleanup = s->need_group_cleanup = FALSE; s->fd_ipv4 = avahi_open_socket_ipv4(); - s->fd_ipv6 = avahi_open_socket_ipv6(); + s->fd_ipv6 = -1 /* avahi_open_socket_ipv6() */ ; if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) { g_critical("Failed to create IP sockets.\n"); @@ -685,11 +707,12 @@ AvahiServer *avahi_server_new(GMainContext *c) { 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->host_name = avahi_get_host_name(); + s->host_name[strcspn(s->host_name, ".")] = 0; - s->hostname = g_strdup_printf("%s.local.", hn); - g_free(hn); + s->domain = avahi_normalize_name("local."); + + s->host_name_fqdn = g_strdup_printf("%s.%s", s->host_name, s->domain); s->record_list = avahi_record_list_new(); @@ -743,7 +766,9 @@ void avahi_server_free(AvahiServer* s) { if (s->fd_ipv6 >= 0) close(s->fd_ipv6); - g_free(s->hostname); + g_free(s->host_name); + g_free(s->domain); + g_free(s->host_name_fqdn); g_source_destroy(s->source); g_source_unref(s->source); @@ -841,7 +866,7 @@ void avahi_server_add_ptr( g_assert(dest); - r = avahi_record_new_full(name ? name : s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR); + r = avahi_record_new_full(name ? name : s->host_name_fqdn, 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); @@ -860,7 +885,7 @@ void avahi_server_add_address( g_assert(s); g_assert(a); - name = name ? (n = avahi_normalize_name(name)) : s->hostname; + name = name ? (n = avahi_normalize_name(name)) : s->host_name_fqdn; if (a->family == AF_INET) { gchar *reverse; @@ -868,12 +893,11 @@ void avahi_server_add_address( 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_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, 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); + avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name); g_free(reverse); } else { @@ -882,17 +906,15 @@ void avahi_server_add_address( 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_server_add(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, 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); + avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, 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); + avahi_server_add_ptr(s, g, interface, protocol, flags | AVAHI_ENTRY_UNIQUE, reverse, name); g_free(reverse); } @@ -912,7 +934,7 @@ void avahi_server_add_text_strlst( g_assert(s); - r = avahi_record_new_full(name ? name : s->hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT); + r = avahi_record_new_full(name ? name : s->host_name_fqdn, 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); @@ -1000,7 +1022,7 @@ void avahi_server_add_service_strlst( domain = "local"; if (!host) - host = s->hostname; + host = s->host_name_fqdn; snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain); snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain); @@ -1080,36 +1102,12 @@ void avahi_server_post_query(AvahiServer *s, gint interface, guchar protocol, Av 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, 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); + if (g->state == state) + return; + g->state = state; if (g->callback) { @@ -1171,3 +1169,15 @@ AvahiEntryGroupState avahi_entry_group_get_state(AvahiEntryGroup *g) { return g->state; } + +const gchar* avahi_server_get_domain(AvahiServer *s) { + g_assert(s); + + return s->domain; +} + +const gchar* avahi_server_get_host_name(AvahiServer *s) { + g_assert(s); + + return s->host_name_fqdn; +} diff --git a/avahi-core/server.h b/avahi-core/server.h index df7857e..420c501 100644 --- a/avahi-core/server.h +++ b/avahi-core/server.h @@ -80,7 +80,7 @@ struct AvahiServer { AvahiTimeEventQueue *time_event_queue; - gchar *hostname; + gchar *host_name, *host_name_fqdn, *domain; gint fd_ipv4, fd_ipv6; @@ -95,7 +95,10 @@ struct AvahiServer { 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_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response); +void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response); +void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast); void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState state); diff --git a/avahi-core/util.c b/avahi-core/util.c index 086c041..9b84d12 100644 --- a/avahi-core/util.c +++ b/avahi-core/util.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "util.h" @@ -401,3 +403,55 @@ gint avahi_domain_hash(const gchar *s) { g_free(n); } } + + +gchar * avahi_alternative_host_name(const gchar *s) { + const gchar *p, *e = NULL; + gchar *c, *r; + gint n; + + g_assert(s); + + for (p = s; *p; p++) + if (!isdigit(*p)) + e = p+1; + + if (e && *e) + n = atoi(e)+1; + else + n = 2; + + c = e ? g_strndup(s, e-s) : g_strdup(s); + r = g_strdup_printf("%s%i", c, n); + g_free(c); + + return r; + +} + +gchar *avahi_alternative_service_name(const gchar *s) { + const gchar *e; + g_assert(s); + + if ((e = strstr(s, " #"))) { + const gchar *n, *p; + e += 2; + + while ((n = strstr(e, " #"))) + e = n + 2; + + for (p = e; *p; p++) + if (!isdigit(*p)) { + e = NULL; + break; + } + } + + if (e) { + gchar *r, *c = g_strndup(s, e-s); + r = g_strdup_printf("%s%i", c, atoi(e)+1); + g_free(c); + return r; + } else + return g_strdup_printf("%s #2", s); +} diff --git a/avahi-core/util.h b/avahi-core/util.h index 6dcecfd..59a0be6 100644 --- a/avahi-core/util.h +++ b/avahi-core/util.h @@ -52,4 +52,8 @@ gchar *avahi_escape_label(const guint8* src, guint src_length, gchar **ret_name, gint avahi_domain_hash(const gchar *s); +gchar *avahi_alternative_host_name(const gchar *s); +gchar *avahi_alternative_service_name(const gchar *s); + + #endif diff --git a/todo b/todo index d3d2f0c..108c20a 100644 --- a/todo +++ b/todo @@ -2,10 +2,10 @@ todo: * Add some APIs to get the clean service name from RR for browsing RFC MUSTs: - * Return to probing state on conflict - * fix flush bit when working on RRsets * one RR too large for single packet +* response job dependencies + * test against apple test suite * release! @@ -36,5 +36,8 @@ done: * support known answer suppresion for incoming unicast queries * check wether RRsets are supported correctly (i.e. that all records of an RRset are really sent if it is requested) (rfc 2181) -* case insensitve comparision - +* case insensitve comparison +* drop records from cache only one second after flush cache bit entry was received +* either send entire RRSET or don't set flush cache bit! +* mantain flush cache bit correctly in psched +* Return to probing state on conflict -- cgit