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\
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
--- /dev/null
+/* $Id$ */
+
+/***
+ This file is part of avahi.
+
+ avahi is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ avahi is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+ Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with avahi; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+
+#include "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);
+}
} 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;
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);
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) {
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);
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) {
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);
}
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);
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);
}
int main(int argc, char *argv[]) {
AvahiServer *avahi;
- gchar *r;
GMainLoop *loop = NULL;
AvahiSubscription *s;
AvahiKey *k;
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;
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);
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);
} 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;
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);
}
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) {
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) {
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);
+}
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);
gboolean avahi_cache_entry_half_ttl(AvahiCache *c, AvahiCacheEntry *e);
+void avahi_cache_flush(AvahiCache *c);
+
#endif
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);
typedef enum {
AVAHI_SUBSCRIPTION_NEW,
AVAHI_SUBSCRIPTION_REMOVE,
- AVAHI_SUBSCRIPTION_CHANGE
} AvahiSubscriptionEvent;
typedef struct AvahiSubscription AvahiSubscription;
} 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);
}
}
} 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;
}
}
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;
}
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);
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);
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;
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);
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;
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;
/* 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) {
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);
/* 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);
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;
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 */
gboolean done;
GTimeVal delivery;
gboolean flush_cache;
+
+ AvahiAddress querier;
+ gboolean querier_valid;
+
AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs);
};
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);
}
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;
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);
}
}
-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);
}
}
}
-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;
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 {
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;
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);
}
}
}
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);
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;
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
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)) {
g_assert(i);
g_assert(a);
+/* g_message("query"); */
+
g_assert(avahi_record_list_empty(s->record_list));
/* Handle the questions */
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;
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);
}
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)
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) {
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--) {
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);
}
}
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;
}
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;
}
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));
/* 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 = {
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");
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();
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);
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);
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;
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 {
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);
}
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);
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);
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) {
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;
+}
AvahiTimeEventQueue *time_event_queue;
- gchar *hostname;
+ gchar *host_name, *host_name_fqdn, *domain;
gint fd_ipv4, fd_ipv6;
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);
#include <errno.h>
#include <limits.h>
#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
#include "util.h"
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);
+}
gint avahi_domain_hash(const gchar *s);
+gchar *avahi_alternative_host_name(const gchar *s);
+gchar *avahi_alternative_service_name(const gchar *s);
+
+
#endif
* 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!
* 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