prioq-test \
strlst-test \
avahi-test \
- alternative-test
+ alternative-test \
+ conformance-test
libavahi_core_la_SOURCES = \
timeeventq.c timeeventq.h\
rr.c rr.h \
dns.c dns.h \
socket.c socket.h \
- psched.c psched.h \
+ response-sched.c response-sched.h \
+ query-sched.c query-sched.h \
+ probe-sched.c probe-sched.h \
announce.c announce.h \
subscribe.c subscribe.h \
strlst.c strlst.h \
avahi_test_SOURCES = \
avahi-test.c \
$(libavahi_core_la_SOURCES)
-
avahi_test_CFLAGS = $(AM_CFLAGS)
avahi_test_LDADD = $(AM_LDADD)
alternative_test_CFLAGS = $(AM_CFLAGS)
alternative_test_LDADD = $(AM_LDADD)
+conformance_test_SOURCES = \
+ conformance-test.c \
+ $(libavahi_core_la_SOURCES)
+conformance_test_CFLAGS = $(AM_CFLAGS)
+conformance_test_LDADD = $(AM_LDADD)
+
valgrind: avahi-test
libtool --mode=execute valgrind ./avahi-test
/* 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_prepare_response(a->server, a->interface, a->entry, FALSE, FALSE);
avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, FALSE);
return;
g = make_goodbye_record(e->record);
- avahi_interface_post_response(i, g, e->flags & AVAHI_ENTRY_UNIQUE, TRUE, NULL);
+ avahi_interface_post_response(i, g, e->flags & AVAHI_ENTRY_UNIQUE, NULL, TRUE);
avahi_record_unref(g);
}
--- /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 <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "core.h"
+#include "util.h"
+
+static gchar *name = NULL;
+static AvahiEntryGroup *group = NULL;
+static int try = 0;
+static AvahiServer *avahi = NULL;
+
+static gboolean dump_timeout(gpointer data) {
+ avahi_server_dump(avahi, stdout);
+ return TRUE;
+}
+
+static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata);
+
+static void create_service(gchar *t) {
+ gchar *n;
+
+ g_assert(t || name);
+
+ if (group)
+ avahi_entry_group_free(group);
+
+ n = t ? g_strdup(t) : avahi_alternative_service_name(name);
+ g_free(name);
+ name = n;
+
+ if (try > 10)
+ sleep(2); /* ugly ugly ugly hack */
+
+ group = avahi_entry_group_new(avahi, entry_group_callback, NULL);
+ avahi_server_add_service(avahi, group, 0, AF_UNSPEC, "_http._tcp", name, NULL, NULL, 80, "foo", NULL);
+ avahi_entry_group_commit(group);
+
+ try++;
+}
+
+static gboolean rename_timeout(gpointer data) {
+
+
+ if (access("flag", F_OK) == 0) {
+ create_service("New - Bonjour Service Name");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata) {
+ if (state == AVAHI_ENTRY_GROUP_COLLISION)
+ create_service(NULL);
+ else if (state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
+ g_message("ESTABLISHED !!!!");
+ try = 0;
+ }
+}
+
+int main(int argc, char *argv[]) {
+ GMainLoop *loop = NULL;
+
+ avahi = avahi_server_new(NULL);
+ create_service("gurke");
+ avahi_server_dump(avahi, stdout);
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_timeout_add(1000*5, dump_timeout, avahi);
+ g_timeout_add(1000*5, rename_timeout, avahi);
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+
+ avahi_entry_group_free(group);
+ avahi_server_free(avahi);
+
+ return 0;
+}
g_message("removing interface %s.%i", i->hardware->name, i->protocol);
avahi_goodbye_interface(m->server, i, send_goodbye);
- g_message("flushing...");
- avahi_packet_scheduler_flush_responses(i->scheduler);
+ g_message("forcing responses...");
+ avahi_response_scheduler_force(i->response_scheduler);
g_message("done");
g_assert(!i->announcements);
while (i->addresses)
free_address(m, i->addresses);
- avahi_packet_scheduler_free(i->scheduler);
+ avahi_response_scheduler_free(i->response_scheduler);
+ avahi_query_scheduler_free(i->query_scheduler);
+ avahi_probe_scheduler_free(i->probe_scheduler);
avahi_cache_free(i->cache);
AVAHI_LLIST_REMOVE(AvahiInterface, interface, m->interfaces, i);
AVAHI_LLIST_HEAD_INIT(AvahiAnnouncement, i->announcements);
i->cache = avahi_cache_new(m->server, i);
- i->scheduler = avahi_packet_scheduler_new(m->server, i);
+ i->response_scheduler = avahi_response_scheduler_new(i);
+ i->query_scheduler = avahi_query_scheduler_new(i);
+ i->probe_scheduler = avahi_probe_scheduler_new(i);
AVAHI_LLIST_PREPEND(AvahiInterface, by_hardware, hw->interfaces, i);
AVAHI_LLIST_PREPEND(AvahiInterface, interface, m->interfaces, i);
avahi_mdns_mcast_leave_ipv6 (i->hardware->index, m->server->fd_ipv6);
avahi_goodbye_interface(m->server, i, FALSE);
+ avahi_response_scheduler_clear(i->response_scheduler);
+ avahi_query_scheduler_clear(i->query_scheduler);
+ avahi_probe_scheduler_clear(i->probe_scheduler);
avahi_cache_flush(i->cache);
i->announcing = FALSE;
g_assert(key);
if (avahi_interface_relevant(i))
- return avahi_packet_scheduler_post_query(i->scheduler, key, immediately);
+ return avahi_query_scheduler_post(i->query_scheduler, key, immediately);
return FALSE;
}
-gboolean avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, gboolean flush_cache, gboolean immediately, const AvahiAddress *querier) {
+gboolean avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, gboolean flush_cache, const AvahiAddress *querier, gboolean immediately) {
g_assert(i);
g_assert(record);
if (avahi_interface_relevant(i))
- return avahi_packet_scheduler_post_response(i->scheduler, record, flush_cache, immediately, querier);
+ return avahi_response_scheduler_post(i->response_scheduler, record, flush_cache, querier, immediately);
return FALSE;
}
g_assert(record);
if (avahi_interface_relevant(i))
- return avahi_packet_scheduler_post_probe(i->scheduler, record, immediately);
+ return avahi_probe_scheduler_post(i->probe_scheduler, record, immediately);
return FALSE;
}
#include "netlink.h"
#include "cache.h"
#include "llist.h"
-#include "psched.h"
+#include "response-sched.h"
+#include "query-sched.h"
+#include "probe-sched.h"
#include "dns.h"
#include "announce.h"
gboolean announcing;
AvahiCache *cache;
- AvahiPacketScheduler *scheduler;
+ AvahiQueryScheduler *query_scheduler;
+ AvahiResponseScheduler * response_scheduler;
+ AvahiProbeScheduler *probe_scheduler;
AVAHI_LLIST_HEAD(AvahiInterfaceAddress, addresses);
AVAHI_LLIST_HEAD(AvahiAnnouncement, announcements);
void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port);
gboolean avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, gboolean immediately);
+gboolean avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, gboolean flush_cache, const AvahiAddress *querier, 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, const AvahiAddress *querier);
void avahi_dump_caches(AvahiInterfaceMonitor *m, FILE *f);
--- /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 "probe-sched.h"
+#include "util.h"
+
+#define AVAHI_PROBE_DEFER_MSEC 70
+
+typedef struct AvahiProbeJob AvahiProbeJob;
+
+struct AvahiProbeJob {
+ AvahiProbeScheduler *scheduler;
+ AvahiTimeEvent *time_event;
+
+ gboolean chosen; /* Use for packet assembling */
+ GTimeVal delivery;
+
+ AvahiRecord *record;
+
+ AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs);
+};
+
+struct AvahiProbeScheduler {
+ AvahiInterface *interface;
+ AvahiTimeEventQueue *time_event_queue;
+
+ AVAHI_LLIST_HEAD(AvahiProbeJob, jobs);
+};
+
+static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record) {
+ AvahiProbeJob *pj;
+
+ g_assert(s);
+ g_assert(record);
+
+ pj = g_new(AvahiProbeJob, 1);
+ pj->scheduler = s;
+ pj->record = avahi_record_ref(record);
+ pj->time_event = NULL;
+ pj->chosen = FALSE;
+
+ AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj);
+
+ return pj;
+}
+
+static void job_free(AvahiProbeScheduler *s, AvahiProbeJob *pj) {
+ g_assert(pj);
+
+ if (pj->time_event)
+ avahi_time_event_queue_remove(s->time_event_queue, pj->time_event);
+
+ AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->jobs, pj);
+
+ avahi_record_unref(pj->record);
+ g_free(pj);
+}
+
+
+AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i) {
+ AvahiProbeScheduler *s;
+
+ g_assert(i);
+
+ s = g_new(AvahiProbeScheduler, 1);
+ s->interface = i;
+ s->time_event_queue = i->monitor->server->time_event_queue;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->jobs);
+
+ return s;
+}
+
+void avahi_probe_scheduler_free(AvahiProbeScheduler *s) {
+ g_assert(s);
+
+ avahi_probe_scheduler_clear(s);
+ g_free(s);
+}
+
+void avahi_probe_scheduler_clear(AvahiProbeScheduler *s) {
+ g_assert(s);
+
+ while (s->jobs)
+ job_free(s, s->jobs);
+}
+
+static gboolean packet_add_probe_query(AvahiProbeScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) {
+ guint size;
+ AvahiKey *k;
+ gboolean b;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(pj);
+
+ g_assert(!pj->chosen);
+
+ /* Estimate the size for this record */
+ size =
+ avahi_key_get_estimate_size(pj->record->key) +
+ avahi_record_get_estimate_size(pj->record);
+
+ /* Too large */
+ if (size > avahi_dns_packet_space(p))
+ return FALSE;
+
+ /* Create the probe query */
+ k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY);
+ b = !!avahi_dns_packet_append_key(p, k, FALSE);
+ g_assert(b);
+
+ /* Mark this job for addition to the packet */
+ pj->chosen = TRUE;
+
+ /* Scan for more jobs whith matching key pattern */
+ for (pj = s->jobs; pj; pj = pj->jobs_next) {
+ if (pj->chosen)
+ continue;
+
+ /* Does the record match the probe? */
+ if (k->class != pj->record->key->class || !avahi_domain_equal(k->name, pj->record->key->name))
+ continue;
+
+ /* This job wouldn't fit in */
+ if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p))
+ break;
+
+ /* Mark this job for addition to the packet */
+ pj->chosen = TRUE;
+ }
+
+ avahi_key_unref(k);
+
+ return TRUE;
+}
+
+static void elapse_callback(AvahiTimeEvent *e, gpointer data) {
+ AvahiProbeJob *pj = data, *next;
+ AvahiProbeScheduler *s;
+ AvahiDnsPacket *p;
+ guint n;
+
+ g_assert(pj);
+ s = pj->scheduler;
+
+ p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
+ n = 1;
+
+ /* Add the import probe */
+ if (!packet_add_probe_query(s, p, pj)) {
+ guint size;
+ AvahiKey *k;
+ gboolean b;
+
+ avahi_dns_packet_free(p);
+
+ /* The probe didn't fit in the package, so let's allocate a larger one */
+
+ size =
+ avahi_key_get_estimate_size(pj->record->key) +
+ avahi_record_get_estimate_size(pj->record) +
+ AVAHI_DNS_PACKET_HEADER_SIZE;
+
+ if (size > AVAHI_DNS_PACKET_MAX_SIZE)
+ size = AVAHI_DNS_PACKET_MAX_SIZE;
+
+ p = avahi_dns_packet_new_query(size);
+
+ k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY);
+ b = avahi_dns_packet_append_key(p, k, FALSE) && avahi_dns_packet_append_record(p, pj->record, FALSE, 0);
+ avahi_key_unref(k);
+
+ if (b) {
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, 1);
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, 1);
+ avahi_interface_send_packet(s->interface, p);
+ } else
+ g_warning("Probe record too large, cannot send");
+
+ avahi_dns_packet_free(p);
+ job_free(s, pj);
+
+ return;
+ }
+
+ /* Try to fill up packet with more probes, if available */
+ for (pj = s->jobs; pj; pj = pj->jobs_next) {
+
+ if (pj->chosen)
+ continue;
+
+ if (!packet_add_probe_query(s, p, pj))
+ break;
+
+ n++;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
+
+ n = 0;
+
+ /* Now add the chosen records to the authorative section */
+ for (pj = s->jobs; pj; pj = next) {
+
+ next = pj->jobs_next;
+
+ if (!pj->chosen)
+ continue;
+
+ if (!avahi_dns_packet_append_record(p, pj->record, FALSE, 0)) {
+ g_warning("Bad probe size estimate!");
+
+ /* Unmark all following jobs */
+ for (; pj; pj = pj->jobs_next)
+ pj->chosen = FALSE;
+
+ break;
+ }
+
+ job_free(s, pj);
+
+ n ++;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n);
+
+ /* Send it now */
+ avahi_interface_send_packet(s->interface, p);
+ avahi_dns_packet_free(p);
+}
+
+gboolean avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, gboolean immediately) {
+ AvahiProbeJob *pj;
+ GTimeVal tv;
+
+ g_assert(s);
+ g_assert(record);
+ g_assert(!avahi_key_is_pattern(record->key));
+
+ avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0);
+
+ /* Create a new job and schedule it */
+ pj = job_new(s, record);
+ pj->delivery = tv;
+ pj->time_event = avahi_time_event_queue_add(s->time_event_queue, &pj->delivery, elapse_callback, pj);
+
+ g_message("Accepted new probe job.");
+
+ return TRUE;
+}
--- /dev/null
+#ifndef fooprobeschedhfoo
+#define fooprobeschedhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of avahi.
+
+ avahi is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ avahi is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+ Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with avahi; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct AvahiProbeScheduler AvahiProbeScheduler;
+
+#include "iface.h"
+#include "address.h"
+
+AvahiProbeScheduler *avahi_probe_scheduler_new(AvahiInterface *i);
+void avahi_probe_scheduler_free(AvahiProbeScheduler *s);
+void avahi_probe_scheduler_clear(AvahiProbeScheduler *s);
+
+gboolean avahi_probe_scheduler_post(AvahiProbeScheduler *s, AvahiRecord *record, gboolean immediately);
+
+#endif
+++ /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 <string.h>
-
-#include "util.h"
-#include "psched.h"
-
-#define AVAHI_QUERY_HISTORY_MSEC 100
-#define AVAHI_QUERY_DEFER_MSEC 100
-#define AVAHI_RESPONSE_HISTORY_MSEC 700
-#define AVAHI_RESPONSE_DEFER_MSEC 20
-#define AVAHI_RESPONSE_JITTER_MSEC 100
-#define AVAHI_PROBE_DEFER_MSEC 70
-
-AvahiPacketScheduler *avahi_packet_scheduler_new(AvahiServer *server, AvahiInterface *i) {
- AvahiPacketScheduler *s;
-
- g_assert(server);
- g_assert(i);
-
- s = g_new(AvahiPacketScheduler, 1);
- s->server = server;
- s->interface = i;
-
- AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->query_jobs);
- AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->response_jobs);
- AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers);
- AVAHI_LLIST_HEAD_INIT(AvahiProbeJob, s->probe_jobs);
-
- return s;
-}
-
-static void query_job_free(AvahiPacketScheduler *s, AvahiQueryJob *qj) {
- g_assert(qj);
-
- if (qj->time_event)
- avahi_time_event_queue_remove(qj->scheduler->server->time_event_queue, qj->time_event);
-
- AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->query_jobs, qj);
-
- avahi_key_unref(qj->key);
- g_free(qj);
-}
-
-static void response_job_free(AvahiPacketScheduler *s, AvahiResponseJob *rj) {
- g_assert(rj);
-
- if (rj->time_event)
- avahi_time_event_queue_remove(rj->scheduler->server->time_event_queue, rj->time_event);
-
- AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->response_jobs, rj);
-
- avahi_record_unref(rj->record);
- g_free(rj);
-}
-
-static void probe_job_free(AvahiPacketScheduler *s, AvahiProbeJob *pj) {
- g_assert(pj);
-
- if (pj->time_event)
- avahi_time_event_queue_remove(pj->scheduler->server->time_event_queue, pj->time_event);
-
- AVAHI_LLIST_REMOVE(AvahiProbeJob, jobs, s->probe_jobs, pj);
-
- avahi_record_unref(pj->record);
- g_free(pj);
-}
-
-void avahi_packet_scheduler_free(AvahiPacketScheduler *s) {
- AvahiQueryJob *qj;
- AvahiResponseJob *rj;
- AvahiProbeJob *pj;
-
- g_assert(s);
-
- g_assert(!s->known_answers);
-
- while ((qj = s->query_jobs))
- query_job_free(s, qj);
- while ((rj = s->response_jobs))
- response_job_free(s, rj);
- while ((pj = s->probe_jobs))
- probe_job_free(s, pj);
-
- g_free(s);
-}
-
-static gpointer known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
- AvahiPacketScheduler *s = userdata;
- AvahiKnownAnswer *ka;
-
- g_assert(c);
- g_assert(pattern);
- g_assert(e);
- g_assert(s);
-
- if (avahi_cache_entry_half_ttl(c, e))
- return NULL;
-
- ka = g_new0(AvahiKnownAnswer, 1);
- ka->scheduler = s;
- ka->record = avahi_record_ref(e->record);
-
- AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka);
- return NULL;
-}
-
-static guint8* packet_add_query_job(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) {
- guint8 *d;
-
- g_assert(s);
- g_assert(p);
- g_assert(qj);
-
- if ((d = avahi_dns_packet_append_key(p, qj->key, FALSE))) {
- GTimeVal tv;
-
- qj->done = 1;
-
- /* Drop query after some time from history */
- avahi_elapse_time(&tv, AVAHI_QUERY_HISTORY_MSEC, 0);
- avahi_time_event_queue_update(s->server->time_event_queue, qj->time_event, &tv);
-
- g_get_current_time(&qj->delivery);
-
- /* Add all matching known answers to the list */
- avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s);
- }
-
- return d;
-}
-
-static void append_known_answers_and_send(AvahiPacketScheduler *s, AvahiDnsPacket *p) {
- AvahiKnownAnswer *ka;
- guint n;
- g_assert(s);
- g_assert(p);
-
- n = 0;
-
- while ((ka = s->known_answers)) {
-
- while (!avahi_dns_packet_append_record(p, ka->record, FALSE, 0)) {
-
- g_assert(!avahi_dns_packet_is_empty(p));
-
- avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC);
- avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
- avahi_interface_send_packet(s->interface, p);
- avahi_dns_packet_free(p);
-
- p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
- n = 0;
- }
-
- AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka);
- avahi_record_unref(ka->record);
- g_free(ka);
-
- n++;
- }
-
- avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
- avahi_interface_send_packet(s->interface, p);
- avahi_dns_packet_free(p);
-}
-
-static void query_elapse(AvahiTimeEvent *e, gpointer data) {
- AvahiQueryJob *qj = data;
- AvahiPacketScheduler *s;
- AvahiDnsPacket *p;
- guint n;
- guint8 *d;
-
- g_assert(qj);
- s = qj->scheduler;
-
- if (qj->done) {
- /* Lets remove it from the history */
- query_job_free(s, qj);
- return;
- }
-
- g_assert(!s->known_answers);
-
- p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
- d = packet_add_query_job(s, p, qj);
- g_assert(d);
- n = 1;
-
- /* Try to fill up packet with more queries, if available */
- for (qj = s->query_jobs; qj; qj = qj->jobs_next) {
-
- if (qj->done)
- continue;
-
- if (!packet_add_query_job(s, p, qj))
- break;
-
- n++;
- }
-
- avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
-
- /* Now add known answers */
- append_known_answers_and_send(s, p);
-}
-
-static AvahiQueryJob* query_job_new(AvahiPacketScheduler *s, AvahiKey *key) {
- AvahiQueryJob *qj;
-
- g_assert(s);
- g_assert(key);
-
- qj = g_new(AvahiQueryJob, 1);
- qj->scheduler = s;
- qj->key = avahi_key_ref(key);
- qj->done = FALSE;
- qj->time_event = NULL;
-
- AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->query_jobs, qj);
-
- return qj;
-}
-
-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;
-
- g_assert(s);
- g_assert(key);
-
- avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0);
-
- if ((qj = look_for_query(s, key))) {
- glong d = avahi_timeval_diff(&tv, &qj->delivery);
-
- /* 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);
- }
-
- qj = query_job_new(s, key);
- qj->delivery = tv;
- qj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &qj->delivery, query_elapse, qj);
- 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;
-
- g_assert(s);
- g_assert(p);
- g_assert(rj);
-
- if ((d = avahi_dns_packet_append_record(p, rj->record, rj->flush_cache, 0))) {
- GTimeVal tv;
-
- rj->done = 1;
-
- /* Drop response after some time from history */
- avahi_elapse_time(&tv, AVAHI_RESPONSE_HISTORY_MSEC, 0);
- avahi_time_event_queue_update(s->server->time_event_queue, rj->time_event, &tv);
-
- g_get_current_time(&rj->delivery);
- }
-
- return d;
-}
-
-static void send_response_packet(AvahiPacketScheduler *s, AvahiResponseJob *rj) {
- AvahiDnsPacket *p;
- guint n;
-
- g_assert(s);
-
- p = avahi_dns_packet_new_response(s->interface->hardware->mtu, TRUE);
- n = 0;
-
- /* If a job was specified, put it in the packet. */
- if (rj) {
- guint8 *d;
- d = packet_add_response_job(s, p, rj);
- g_assert(d);
- n++;
- }
-
- /* Try to fill up packet with more responses, if available */
- for (rj = s->response_jobs; rj; rj = rj->jobs_next) {
-
- if (rj->done)
- continue;
-
- if (!packet_add_response_job(s, p, rj))
- break;
-
- n++;
- }
-
- avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
- avahi_interface_send_packet(s->interface, p);
- avahi_dns_packet_free(p);
-}
-
-static void response_elapse(AvahiTimeEvent *e, gpointer data) {
- AvahiResponseJob *rj = data;
- AvahiPacketScheduler *s;
-
- g_assert(rj);
- s = rj->scheduler;
-
- if (rj->done) {
- /* Lets remove it from the history */
- response_job_free(s, rj);
- return;
- }
-
- send_response_packet(s, rj);
-}
-
-static AvahiResponseJob* look_for_response(AvahiPacketScheduler *s, AvahiRecord *record) {
- AvahiResponseJob *rj;
-
- g_assert(s);
- g_assert(record);
-
- for (rj = s->response_jobs; rj; rj = rj->jobs_next)
- if (avahi_record_equal_no_ttl(rj->record, record))
- return rj;
-
- return NULL;
-}
-
-static AvahiResponseJob* response_job_new(AvahiPacketScheduler *s, AvahiRecord *record) {
- AvahiResponseJob *rj;
-
- g_assert(s);
- g_assert(record);
-
- rj = g_new(AvahiResponseJob, 1);
- rj->scheduler = s;
- rj->record = avahi_record_ref(record);
- rj->done = FALSE;
- rj->time_event = NULL;
- rj->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, const AvahiAddress *querier) {
- AvahiResponseJob *rj;
- GTimeVal tv;
-
- g_assert(s);
- g_assert(record);
-
- g_assert(!avahi_key_is_pattern(record->key));
-
- avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC);
-
- /* Don't send out duplicates */
-
- if ((rj = look_for_response(s, record))) {
-
- glong d = 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) &&
- (rj->flush_cache || !flush_cache) &&
- ((!rj->done && d >= 0) || (rj->done && d <= AVAHI_RESPONSE_HISTORY_MSEC*1000))) {
-
- g_message("Duplicate suppresion active.");
- return FALSE;
- }
-
- /* 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);
- }
-
- /* 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);
-
- if ((rj->querier_valid = !!querier))
- rj->querier = *querier;
-
- return TRUE;
-}
-
-void response_job_set_elapse_time(AvahiPacketScheduler *s, AvahiResponseJob *rj, guint msec, guint jitter) {
- GTimeVal tv;
-
- g_assert(s);
- g_assert(rj);
-
- avahi_elapse_time(&tv, msec, jitter);
-
- if (rj->time_event)
- avahi_time_event_queue_update(s->server->time_event_queue, rj->time_event, &tv);
- else
- rj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &tv, response_elapse, rj);
-}
-
-void avahi_packet_scheduler_incoming_response(AvahiPacketScheduler *s, AvahiRecord *record, 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". */
-
- if ((rj = look_for_response(s, record))) {
-
- if (!rj->done) {
-
- 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;
-
-
- 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 */
- 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;
-
- g_assert(s);
-
- /* Send all scheduled responses, ignoring the scheduled time */
-
- for (rj = s->response_jobs; rj; rj = rj->jobs_next)
- if (!rj->done)
- send_response_packet(s, rj);
-}
-
-static AvahiProbeJob* probe_job_new(AvahiPacketScheduler *s, AvahiRecord *record) {
- AvahiProbeJob *pj;
-
- g_assert(s);
- g_assert(record);
-
- pj = g_new(AvahiProbeJob, 1);
- pj->scheduler = s;
- pj->record = avahi_record_ref(record);
- pj->time_event = NULL;
- pj->chosen = FALSE;
-
- AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->probe_jobs, pj);
-
- return pj;
-}
-
-static guint8* packet_add_probe_query(AvahiPacketScheduler *s, AvahiDnsPacket *p, AvahiProbeJob *pj) {
- guint size;
- guint8 *ret;
- AvahiKey *k;
-
- g_assert(s);
- g_assert(p);
- g_assert(pj);
-
- g_assert(!pj->chosen);
-
- /* Estimate the size for this record */
- size =
- avahi_key_get_estimate_size(pj->record->key) +
- avahi_record_get_estimate_size(pj->record);
-
- /* Too large */
- if (size > avahi_dns_packet_space(p))
- return NULL;
-
- /* Create the probe query */
- k = avahi_key_new(pj->record->key->name, pj->record->key->class, AVAHI_DNS_TYPE_ANY);
- ret = avahi_dns_packet_append_key(p, k, FALSE);
- g_assert(ret);
-
- /* Mark this job for addition to the packet */
- pj->chosen = TRUE;
-
- /* Scan for more jobs whith matching key pattern */
- for (pj = s->probe_jobs; pj; pj = pj->jobs_next) {
- if (pj->chosen)
- continue;
-
- /* Does the record match the probe? */
- if (k->class != pj->record->key->class || !avahi_domain_equal(k->name, pj->record->key->name))
- continue;
-
- /* This job wouldn't fit in */
- if (avahi_record_get_estimate_size(pj->record) > avahi_dns_packet_space(p))
- break;
-
- /* Mark this job for addition to the packet */
- pj->chosen = TRUE;
- }
-
- avahi_key_unref(k);
-
- return ret;
-}
-
-static void probe_elapse(AvahiTimeEvent *e, gpointer data) {
- AvahiProbeJob *pj = data, *next;
- AvahiPacketScheduler *s;
- AvahiDnsPacket *p;
- guint n;
-
- g_assert(pj);
- s = pj->scheduler;
-
- p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
-
- /* Add the import probe */
- if (!packet_add_probe_query(s, p, pj)) {
- g_warning("Record too large! ---");
- avahi_dns_packet_free(p);
- return;
- }
-
- n = 1;
-
- /* Try to fill up packet with more probes, if available */
- for (pj = s->probe_jobs; pj; pj = pj->jobs_next) {
-
- if (pj->chosen)
- continue;
-
- if (!packet_add_probe_query(s, p, pj))
- break;
-
- n++;
- }
-
- avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
-
- n = 0;
-
- /* Now add the chosen records to the authorative section */
- for (pj = s->probe_jobs; pj; pj = next) {
-
- next = pj->jobs_next;
-
- if (!pj->chosen)
- continue;
-
- if (!avahi_dns_packet_append_record(p, pj->record, FALSE, 0)) {
- g_warning("Bad probe size estimate!");
-
- /* Unmark all following jobs */
- for (; pj; pj = pj->jobs_next)
- pj->chosen = FALSE;
-
- break;
- }
-
- probe_job_free(s, pj);
-
- n ++;
- }
-
- avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_NSCOUNT, n);
-
- /* Send it now */
- avahi_interface_send_packet(s->interface, p);
- avahi_dns_packet_free(p);
-}
-
-gboolean avahi_packet_scheduler_post_probe(AvahiPacketScheduler *s, AvahiRecord *record, gboolean immediately) {
- AvahiProbeJob *pj;
- GTimeVal tv;
-
- g_assert(s);
- g_assert(record);
- g_assert(!avahi_key_is_pattern(record->key));
-
- avahi_elapse_time(&tv, immediately ? 0 : AVAHI_PROBE_DEFER_MSEC, 0);
-
- /* Create a new job and schedule it */
- pj = probe_job_new(s, record);
- pj->delivery = tv;
- pj->time_event = avahi_time_event_queue_add(s->server->time_event_queue, &pj->delivery, probe_elapse, pj);
-
- return TRUE;
-}
+++ /dev/null
-#ifndef foopschedhfoo
-#define foopschedhfoo
-
-/* $Id$ */
-
-/***
- This file is part of avahi.
-
- avahi is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as
- published by the Free Software Foundation; either version 2.1 of the
- License, or (at your option) any later version.
-
- avahi is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
- Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with avahi; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- USA.
-***/
-
-typedef struct AvahiQueryJob AvahiQueryJob;
-typedef struct AvahiResponseJob AvahiResponseJob;
-typedef struct AvahiPacketScheduler AvahiPacketScheduler;
-typedef struct AvahiKnownAnswer AvahiKnownAnswer;
-typedef struct AvahiProbeJob AvahiProbeJob;
-
-#include "timeeventq.h"
-#include "rr.h"
-#include "llist.h"
-#include "iface.h"
-
-struct AvahiQueryJob {
- AvahiPacketScheduler *scheduler;
- AvahiTimeEvent *time_event;
- AvahiKey *key;
- gboolean done;
- GTimeVal delivery;
- AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs);
-};
-
-struct AvahiResponseJob {
- AvahiPacketScheduler *scheduler;
- AvahiTimeEvent *time_event;
- AvahiRecord *record;
- gboolean done;
- GTimeVal delivery;
- gboolean flush_cache;
-
- AvahiAddress querier;
- gboolean querier_valid;
-
- AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs);
-};
-
-struct AvahiKnownAnswer {
- AvahiPacketScheduler *scheduler;
- AvahiRecord *record;
-
- AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer);
-};
-
-struct AvahiProbeJob {
- AvahiPacketScheduler *scheduler;
- AvahiTimeEvent *time_event;
- AvahiRecord *record;
-
- gboolean chosen; /* Use for packet assembling */
- GTimeVal delivery;
-
- AVAHI_LLIST_FIELDS(AvahiProbeJob, jobs);
-};
-
-struct AvahiPacketScheduler {
- AvahiServer *server;
-
- AvahiInterface *interface;
-
- AVAHI_LLIST_HEAD(AvahiQueryJob, query_jobs);
- AVAHI_LLIST_HEAD(AvahiResponseJob, response_jobs);
- AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers);
- AVAHI_LLIST_HEAD(AvahiProbeJob, probe_jobs);
-};
-
-AvahiPacketScheduler *avahi_packet_scheduler_new(AvahiServer *server, AvahiInterface *i);
-void avahi_packet_scheduler_free(AvahiPacketScheduler *s);
-
-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, 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, 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);
-
-#endif
--- /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 "query-sched.h"
+#include "util.h"
+
+#define AVAHI_QUERY_HISTORY_MSEC 100
+#define AVAHI_QUERY_DEFER_MSEC 100
+
+typedef struct AvahiQueryJob AvahiQueryJob;
+typedef struct AvahiKnownAnswer AvahiKnownAnswer;
+
+struct AvahiQueryJob {
+ AvahiQueryScheduler *scheduler;
+ AvahiTimeEvent *time_event;
+
+ gboolean done;
+ GTimeVal delivery;
+
+ AvahiKey *key;
+
+ AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs);
+};
+
+struct AvahiKnownAnswer {
+ AvahiQueryScheduler *scheduler;
+ AvahiRecord *record;
+
+ AVAHI_LLIST_FIELDS(AvahiKnownAnswer, known_answer);
+};
+
+struct AvahiQueryScheduler {
+ AvahiInterface *interface;
+ AvahiTimeEventQueue *time_event_queue;
+
+ AVAHI_LLIST_HEAD(AvahiQueryJob, jobs);
+ AVAHI_LLIST_HEAD(AvahiQueryJob, history);
+ AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers);
+};
+
+static AvahiQueryJob* job_new(AvahiQueryScheduler *s, AvahiKey *key, gboolean done) {
+ AvahiQueryJob *qj;
+
+ g_assert(s);
+ g_assert(key);
+
+ qj = g_new(AvahiQueryJob, 1);
+ qj->scheduler = s;
+ qj->key = avahi_key_ref(key);
+ qj->time_event = NULL;
+
+ if ((qj->done = done))
+ AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj);
+ else
+ AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->jobs, qj);
+
+ return qj;
+}
+
+static void job_free(AvahiQueryScheduler *s, AvahiQueryJob *qj) {
+ g_assert(s);
+ g_assert(qj);
+
+ if (qj->time_event)
+ avahi_time_event_queue_remove(s->time_event_queue, qj->time_event);
+
+ if (qj->done)
+ AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->history, qj);
+ else
+ AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj);
+
+ avahi_key_unref(qj->key);
+ g_free(qj);
+}
+
+static void elapse_callback(AvahiTimeEvent *e, gpointer data);
+
+static void job_set_elapse_time(AvahiQueryScheduler *s, AvahiQueryJob *qj, guint msec, guint jitter) {
+ GTimeVal tv;
+
+ g_assert(s);
+ g_assert(qj);
+
+ avahi_elapse_time(&tv, msec, jitter);
+
+ if (qj->time_event)
+ avahi_time_event_queue_update(s->time_event_queue, qj->time_event, &tv);
+ else
+ qj->time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, elapse_callback, qj);
+}
+
+static void job_mark_done(AvahiQueryScheduler *s, AvahiQueryJob *qj) {
+ g_assert(s);
+ g_assert(qj);
+
+ g_assert(!qj->done);
+
+ AVAHI_LLIST_REMOVE(AvahiQueryJob, jobs, s->jobs, qj);
+ AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj);
+
+ qj->done = TRUE;
+
+ job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0);
+ g_get_current_time(&qj->delivery);
+}
+
+AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i) {
+ AvahiQueryScheduler *s;
+ g_assert(i);
+
+ s = g_new(AvahiQueryScheduler, 1);
+ s->interface = i;
+ s->time_event_queue = i->monitor->server->time_event_queue;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->jobs);
+ AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->history);
+ AVAHI_LLIST_HEAD_INIT(AvahiKnownAnswer, s->known_answers);
+
+ return s;
+}
+
+void avahi_query_scheduler_free(AvahiQueryScheduler *s) {
+ g_assert(s);
+
+ g_assert(!s->known_answers);
+ avahi_query_scheduler_clear(s);
+ g_free(s);
+}
+
+void avahi_query_scheduler_clear(AvahiQueryScheduler *s) {
+ g_assert(s);
+
+ while (s->jobs)
+ job_free(s, s->jobs);
+ while (s->history)
+ job_free(s, s->history);
+}
+
+static gpointer known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, gpointer userdata) {
+ AvahiQueryScheduler *s = userdata;
+ AvahiKnownAnswer *ka;
+
+ g_assert(c);
+ g_assert(pattern);
+ g_assert(e);
+ g_assert(s);
+
+ if (avahi_cache_entry_half_ttl(c, e))
+ return NULL;
+
+ ka = g_new0(AvahiKnownAnswer, 1);
+ ka->scheduler = s;
+ ka->record = avahi_record_ref(e->record);
+
+ AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka);
+ return NULL;
+}
+
+static gboolean packet_add_query_job(AvahiQueryScheduler *s, AvahiDnsPacket *p, AvahiQueryJob *qj) {
+ g_assert(s);
+ g_assert(p);
+ g_assert(qj);
+
+ if (!avahi_dns_packet_append_key(p, qj->key, FALSE))
+ return FALSE;
+
+ /* Add all matching known answers to the list */
+ avahi_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s);
+
+ job_mark_done(s, qj);
+
+ return TRUE;
+}
+
+static void append_known_answers_and_send(AvahiQueryScheduler *s, AvahiDnsPacket *p) {
+ AvahiKnownAnswer *ka;
+ guint n;
+ g_assert(s);
+ g_assert(p);
+
+ n = 0;
+
+ while ((ka = s->known_answers)) {
+ gboolean too_large = FALSE;
+
+ while (!avahi_dns_packet_append_record(p, ka->record, FALSE, 0)) {
+
+ if (avahi_dns_packet_is_empty(p)) {
+ /* The record is too large to fit into one packet, so
+ there's no point in sending it. Better is letting
+ the owner of the record send it as a response. This
+ has the advantage of a cache refresh. */
+
+ too_large = TRUE;
+ break;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_FLAGS, avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) | AVAHI_DNS_FLAG_TC);
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
+ avahi_interface_send_packet(s->interface, p);
+ avahi_dns_packet_free(p);
+
+ p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
+ n = 0;
+ }
+
+ AVAHI_LLIST_REMOVE(AvahiKnownAnswer, known_answer, s->known_answers, ka);
+ avahi_record_unref(ka->record);
+ g_free(ka);
+
+ if (!too_large)
+ n++;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
+ avahi_interface_send_packet(s->interface, p);
+ avahi_dns_packet_free(p);
+}
+
+static void elapse_callback(AvahiTimeEvent *e, gpointer data) {
+ AvahiQueryJob *qj = data;
+ AvahiQueryScheduler *s;
+ AvahiDnsPacket *p;
+ guint n;
+ gboolean b;
+
+ g_assert(qj);
+ s = qj->scheduler;
+
+ if (qj->done) {
+ /* Lets remove it from the history */
+ job_free(s, qj);
+ return;
+ }
+
+ g_assert(!s->known_answers);
+
+ p = avahi_dns_packet_new_query(s->interface->hardware->mtu);
+ b = packet_add_query_job(s, p, qj);
+ g_assert(b); /* An query must always fit in */
+ n = 1;
+
+ /* Try to fill up packet with more queries, if available */
+ while (s->jobs) {
+
+ if (!packet_add_query_job(s, p, s->jobs))
+ break;
+
+ n++;
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_QDCOUNT, n);
+
+ /* Now add known answers */
+ append_known_answers_and_send(s, p);
+}
+
+static AvahiQueryJob* find_scheduled_job(AvahiQueryScheduler *s, AvahiKey *key) {
+ AvahiQueryJob *qj;
+
+ g_assert(s);
+ g_assert(key);
+
+ for (qj = s->jobs; qj; qj = qj->jobs_next) {
+ g_assert(!qj->done);
+
+ if (avahi_key_equal(qj->key, key))
+ return qj;
+ }
+
+ return NULL;
+}
+
+static AvahiQueryJob* find_history_job(AvahiQueryScheduler *s, AvahiKey *key) {
+ AvahiQueryJob *qj;
+
+ g_assert(s);
+ g_assert(key);
+
+ for (qj = s->history; qj; qj = qj->jobs_next) {
+ g_assert(qj->done);
+
+ if (avahi_key_equal(qj->key, key)) {
+ /* Check whether this entry is outdated */
+
+ if (avahi_age(&qj->delivery) > AVAHI_QUERY_HISTORY_MSEC*1000) {
+ /* it is outdated, so let's remove it */
+ job_free(s, qj);
+ return NULL;
+ }
+
+ return qj;
+ }
+ }
+
+ return NULL;
+}
+
+gboolean avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, gboolean immediately) {
+ GTimeVal tv;
+ AvahiQueryJob *qj;
+
+ g_assert(s);
+ g_assert(key);
+
+ if ((qj = find_history_job(s, key))) {
+ g_message("Query suppressed by local duplicate suppression (history)");
+ return FALSE;
+ }
+
+ avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0);
+
+ if ((qj = find_scheduled_job(s, key))) {
+ /* Duplicate questions suppression */
+
+ g_message("Query suppressed by local duplicate suppression (scheduled)");
+
+ if (avahi_timeval_compare(&tv, &qj->delivery) < 0) {
+ /* If the new entry should be scheduled earlier,
+ * update the old entry */
+ qj->delivery = tv;
+ avahi_time_event_queue_update(s->time_event_queue, qj->time_event, &qj->delivery);
+ }
+
+ return TRUE;
+ } else {
+ g_message("Accepted new query job.\n");
+
+ qj = job_new(s, key, FALSE);
+ qj->delivery = tv;
+ qj->time_event = avahi_time_event_queue_add(s->time_event_queue, &qj->delivery, elapse_callback, qj);
+
+ return TRUE;
+ }
+}
+
+void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) {
+ AvahiQueryJob *qj;
+
+ g_assert(s);
+ g_assert(key);
+
+ /* This function is called whenever an incoming query was
+ * receieved. We drop scheduled queries that match. The keyword is
+ * "DUPLICATE QUESTION SUPPRESION". */
+
+ if ((qj = find_scheduled_job(s, key))) {
+ g_message("Query suppressed by distributed duplicate suppression");
+ job_mark_done(s, qj);
+ return;
+ }
+
+ qj = job_new(s, key, TRUE);
+ g_get_current_time(&qj->delivery);
+ job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0);
+}
+
--- /dev/null
+#ifndef fooqueryschedhfoo
+#define fooqueryschedhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of avahi.
+
+ avahi is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ avahi is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+ Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with avahi; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct AvahiQueryScheduler AvahiQueryScheduler;
+
+#include "iface.h"
+#include "address.h"
+
+AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i);
+void avahi_query_scheduler_free(AvahiQueryScheduler *s);
+void avahi_query_scheduler_clear(AvahiQueryScheduler *s);
+
+gboolean avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, gboolean immediately);
+void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key);
+
+#endif
--- /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 "response-sched.h"
+#include "util.h"
+
+#define AVAHI_RESPONSE_HISTORY_MSEC 700
+#define AVAHI_RESPONSE_DEFER_MSEC 20
+#define AVAHI_RESPONSE_JITTER_MSEC 100
+#define AVAHI_RESPONSE_SUPPRESS_MSEC 700
+
+typedef struct AvahiResponseJob AvahiResponseJob;
+
+typedef enum {
+ AVAHI_SCHEDULED,
+ AVAHI_DONE,
+ AVAHI_SUPPRESSED
+} AvahiResponseJobState;
+
+struct AvahiResponseJob {
+ AvahiResponseScheduler *scheduler;
+ AvahiTimeEvent *time_event;
+
+ AvahiResponseJobState state;
+ GTimeVal delivery;
+
+ AvahiRecord *record;
+ gboolean flush_cache;
+ AvahiAddress querier;
+ gboolean querier_valid;
+
+ AVAHI_LLIST_FIELDS(AvahiResponseJob, jobs);
+};
+
+struct AvahiResponseScheduler {
+ AvahiInterface *interface;
+ AvahiTimeEventQueue *time_event_queue;
+
+ AVAHI_LLIST_HEAD(AvahiResponseJob, jobs);
+ AVAHI_LLIST_HEAD(AvahiResponseJob, history);
+ AVAHI_LLIST_HEAD(AvahiResponseJob, suppressed);
+};
+
+static AvahiResponseJob* job_new(AvahiResponseScheduler *s, AvahiRecord *record, AvahiResponseJobState state) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+ g_assert(record);
+
+ rj = g_new(AvahiResponseJob, 1);
+ rj->scheduler = s;
+ rj->record = avahi_record_ref(record);
+ rj->time_event = NULL;
+ rj->flush_cache = FALSE;
+ rj->querier_valid = FALSE;
+
+ if ((rj->state = state) == AVAHI_SCHEDULED)
+ AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->jobs, rj);
+ else if (rj->state == AVAHI_DONE)
+ AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj);
+ else /* rj->state == AVAHI_SUPPRESSED */
+ AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->suppressed, rj);
+
+ return rj;
+}
+
+static void job_free(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
+ g_assert(s);
+ g_assert(rj);
+
+ if (rj->time_event)
+ avahi_time_event_queue_remove(s->time_event_queue, rj->time_event);
+
+ if (rj->state == AVAHI_SCHEDULED)
+ AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj);
+ else if (rj->state == AVAHI_DONE)
+ AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->history, rj);
+ else /* rj->state == AVAHI_SUPPRESSED */
+ AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->suppressed, rj);
+
+ avahi_record_unref(rj->record);
+ g_free(rj);
+}
+
+static void elapse_callback(AvahiTimeEvent *e, gpointer data);
+
+static void job_set_elapse_time(AvahiResponseScheduler *s, AvahiResponseJob *rj, guint msec, guint jitter) {
+ GTimeVal tv;
+
+ g_assert(s);
+ g_assert(rj);
+
+ avahi_elapse_time(&tv, msec, jitter);
+
+ if (rj->time_event)
+ avahi_time_event_queue_update(s->time_event_queue, rj->time_event, &tv);
+ else
+ rj->time_event = avahi_time_event_queue_add(s->time_event_queue, &tv, elapse_callback, rj);
+}
+
+static void job_mark_done(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
+ g_assert(s);
+ g_assert(rj);
+
+ g_assert(rj->state == AVAHI_SCHEDULED);
+
+ AVAHI_LLIST_REMOVE(AvahiResponseJob, jobs, s->jobs, rj);
+ AVAHI_LLIST_PREPEND(AvahiResponseJob, jobs, s->history, rj);
+
+ rj->state = AVAHI_DONE;
+
+ job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0);
+
+ g_get_current_time(&rj->delivery);
+}
+
+AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i) {
+ AvahiResponseScheduler *s;
+ g_assert(i);
+
+ s = g_new(AvahiResponseScheduler, 1);
+ s->interface = i;
+ s->time_event_queue = i->monitor->server->time_event_queue;
+
+ AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->jobs);
+ AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->history);
+ AVAHI_LLIST_HEAD_INIT(AvahiResponseJob, s->suppressed);
+
+ return s;
+}
+
+void avahi_response_scheduler_free(AvahiResponseScheduler *s) {
+ g_assert(s);
+
+ avahi_response_scheduler_clear(s);
+ g_free(s);
+}
+
+void avahi_response_scheduler_clear(AvahiResponseScheduler *s) {
+ g_assert(s);
+
+ while (s->jobs)
+ job_free(s, s->jobs);
+ while (s->history)
+ job_free(s, s->history);
+ while (s->suppressed)
+ job_free(s, s->suppressed);
+}
+
+static void enumerate_aux_records_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
+ AvahiResponseJob *rj = userdata;
+
+ g_assert(r);
+ g_assert(rj);
+
+ avahi_response_scheduler_post(rj->scheduler, r, flush_cache, rj->querier_valid ? &rj->querier : NULL, FALSE);
+}
+
+static gboolean packet_add_response_job(AvahiResponseScheduler *s, AvahiDnsPacket *p, AvahiResponseJob *rj) {
+ g_assert(s);
+ g_assert(p);
+ g_assert(rj);
+
+ /* Try to add this record to the packet */
+ if (!avahi_dns_packet_append_record(p, rj->record, rj->flush_cache, 0))
+ return FALSE;
+
+ /* Ok, this record will definitely be sent, so schedule the
+ * auxilliary packets, too */
+ avahi_server_enumerate_aux_records(s->interface->monitor->server, s->interface, rj->record, enumerate_aux_records_callback, rj);
+ job_mark_done(s, rj);
+
+ return TRUE;
+}
+
+static void send_response_packet(AvahiResponseScheduler *s, AvahiResponseJob *rj) {
+ AvahiDnsPacket *p;
+ guint n;
+
+ g_assert(s);
+ g_assert(rj);
+
+ p = avahi_dns_packet_new_response(s->interface->hardware->mtu, TRUE);
+ n = 1;
+
+ /* Put it in the packet. */
+ if (packet_add_response_job(s, p, rj)) {
+
+ /* Try to fill up packet with more responses, if available */
+ while (s->jobs) {
+
+ if (!packet_add_response_job(s, p, s->jobs))
+ break;
+
+ n++;
+ }
+
+ } else {
+ guint size;
+
+ avahi_dns_packet_free(p);
+
+ /* OK, the packet was too small, so create one that fits */
+ size = avahi_record_get_estimate_size(rj->record) + AVAHI_DNS_PACKET_HEADER_SIZE;
+
+ if (size > AVAHI_DNS_PACKET_MAX_SIZE)
+ size = AVAHI_DNS_PACKET_MAX_SIZE;
+
+ p = avahi_dns_packet_new_response(size, TRUE);
+
+ if (!packet_add_response_job(s, p, rj)) {
+ avahi_dns_packet_free(p);
+
+ g_warning("Record too large, cannot send");
+ job_mark_done(s, rj);
+ return;
+ }
+ }
+
+ avahi_dns_packet_set_field(p, AVAHI_DNS_FIELD_ANCOUNT, n);
+ avahi_interface_send_packet(s->interface, p);
+ avahi_dns_packet_free(p);
+}
+
+static void elapse_callback(AvahiTimeEvent *e, gpointer data) {
+ AvahiResponseJob *rj = data;
+
+ g_assert(rj);
+
+ if (rj->state == AVAHI_DONE || rj->state == AVAHI_SUPPRESSED)
+ job_free(rj->scheduler, rj); /* Lets drop this entry */
+ else
+ send_response_packet(rj->scheduler, rj);
+}
+
+static AvahiResponseJob* find_scheduled_job(AvahiResponseScheduler *s, AvahiRecord *record) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+ g_assert(record);
+
+ for (rj = s->jobs; rj; rj = rj->jobs_next) {
+ g_assert(rj->state == AVAHI_SCHEDULED);
+
+ if (avahi_record_equal_no_ttl(rj->record, record))
+ return rj;
+ }
+
+ return NULL;
+}
+
+static AvahiResponseJob* find_history_job(AvahiResponseScheduler *s, AvahiRecord *record) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+ g_assert(record);
+
+ for (rj = s->history; rj; rj = rj->jobs_next) {
+ g_assert(rj->state == AVAHI_DONE);
+
+ if (avahi_record_equal_no_ttl(rj->record, record)) {
+ /* Check whether this entry is outdated */
+
+ if (avahi_age(&rj->delivery) > AVAHI_RESPONSE_HISTORY_MSEC*1000) {
+ /* it is outdated, so let's remove it */
+ job_free(s, rj);
+ return NULL;
+ }
+
+ return rj;
+ }
+ }
+
+ return NULL;
+}
+
+static AvahiResponseJob* find_suppressed_job(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+ g_assert(record);
+ g_assert(querier);
+
+ for (rj = s->suppressed; rj; rj = rj->jobs_next) {
+ g_assert(rj->state == AVAHI_SUPPRESSED);
+ g_assert(rj->querier_valid);
+
+ if (avahi_record_equal_no_ttl(rj->record, record) &&
+ avahi_address_cmp(&rj->querier, querier) == 0) {
+ /* Check whether this entry is outdated */
+
+ if (avahi_age(&rj->delivery) > AVAHI_RESPONSE_SUPPRESS_MSEC*1000) {
+ /* it is outdated, so let's remove it */
+ job_free(s, rj);
+ return NULL;
+ }
+
+ return rj;
+ }
+ }
+
+ return NULL;
+}
+
+gboolean avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, gboolean flush_cache, const AvahiAddress *querier, gboolean immediately) {
+ AvahiResponseJob *rj;
+ GTimeVal tv;
+
+ g_assert(s);
+ g_assert(record);
+
+ g_assert(!avahi_key_is_pattern(record->key));
+
+ /* Check whether this response is suppressed */
+ if (querier &&
+ (rj = find_suppressed_job(s, record, querier)) &&
+ avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) &&
+ rj->record->ttl >= record->ttl/2) {
+
+ g_message("Response suppressed by known answer suppression.");
+ return FALSE;
+ }
+
+ /* Check if we already sent this response recently */
+ if ((rj = find_history_job(s, record))) {
+
+ if (avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) &&
+ rj->record->ttl >= record->ttl/2 &&
+ (rj->flush_cache || !flush_cache)) {
+ g_message("Response suppressed by local duplicate suppression (history)");
+ return FALSE;
+ }
+
+ /* Outdated ... */
+ job_free(s, rj);
+ }
+
+ avahi_elapse_time(&tv, immediately ? 0 : AVAHI_RESPONSE_DEFER_MSEC, immediately ? 0 : AVAHI_RESPONSE_JITTER_MSEC);
+
+ if ((rj = find_scheduled_job(s, record))) {
+ g_message("Response suppressed by local duplicate suppression (scheduled)");
+
+ /* Update a little ... */
+
+ /* Update the time if the new is prior to the old */
+ if (avahi_timeval_compare(&tv, &rj->delivery) < 0) {
+ rj->delivery = tv;
+ avahi_time_event_queue_update(s->time_event_queue, rj->time_event, &rj->delivery);
+ }
+
+ /* Update the flush cache bit */
+ if (flush_cache)
+ rj->flush_cache = TRUE;
+
+ /* Update the querier field */
+ if (!querier || (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) != 0))
+ rj->querier_valid = FALSE;
+
+ /* Update record data (just for the TTL) */
+ avahi_record_unref(rj->record);
+ rj->record = avahi_record_ref(record);
+
+ return TRUE;
+ } else {
+ g_message("Accepted new response job.");
+
+ /* Create a new job and schedule it */
+ rj = job_new(s, record, AVAHI_SCHEDULED);
+ rj->delivery = tv;
+ rj->time_event = avahi_time_event_queue_add(s->time_event_queue, &rj->delivery, elapse_callback, rj);
+ rj->flush_cache = flush_cache;
+
+ if ((rj->querier_valid = !!querier))
+ rj->querier = *querier;
+
+ return TRUE;
+ }
+}
+
+void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, gboolean flush_cache) {
+ AvahiResponseJob *rj;
+ g_assert(s);
+
+ /* This function is called whenever an incoming response was
+ * receieved. We drop scheduled responses which match here. The
+ * keyword is "DUPLICATE ANSWER SUPPRESION". */
+
+ if ((rj = find_scheduled_job(s, record))) {
+
+ if ((!rj->flush_cache || flush_cache) && /* flush cache bit was set correctly */
+ avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */
+ record->ttl >= rj->record->ttl/2) { /* sensible TTL */
+
+ /* A matching entry was found, so let's mark it done */
+ g_message("Response suppressed by distributed duplicate suppression");
+ job_mark_done(s, rj);
+ }
+
+ return;
+ }
+
+ if ((rj = find_history_job(s, record))) {
+ /* Found a history job, let's update it */
+ avahi_record_unref(rj->record);
+ rj->record = avahi_record_ref(record);
+ } else
+ /* Found no existing history job, so let's create a new one */
+ rj = job_new(s, record, AVAHI_DONE);
+
+ rj->flush_cache = flush_cache;
+ rj->querier_valid = FALSE;
+
+ g_get_current_time(&rj->delivery);
+ job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0);
+}
+
+void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) {
+ AvahiResponseJob *rj;
+
+ g_assert(s);
+ g_assert(record);
+ g_assert(querier);
+
+ if ((rj = find_scheduled_job(s, record))) {
+
+ if (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) == 0 && /* same originator */
+ avahi_record_is_goodbye(record) == avahi_record_is_goodbye(rj->record) && /* both goodbye packets, or both not */
+ record->ttl >= rj->record->ttl/2) { /* sensible TTL */
+
+ /* A matching entry was found, so let's drop it */
+ g_message("Known answer suppression active!");
+ job_free(s, rj);
+ }
+ }
+
+ if ((rj = find_suppressed_job(s, record, querier))) {
+
+ /* Let's update the old entry */
+ avahi_record_unref(rj->record);
+ rj->record = avahi_record_ref(record);
+
+ } else {
+
+ /* Create a new entry */
+ rj = job_new(s, record, AVAHI_SUPPRESSED);
+ rj->querier_valid = TRUE;
+ rj->querier = *querier;
+ }
+
+ g_get_current_time(&rj->delivery);
+ job_set_elapse_time(s, rj, AVAHI_RESPONSE_SUPPRESS_MSEC, 0);
+}
+
+void avahi_response_scheduler_force(AvahiResponseScheduler *s) {
+ g_assert(s);
+
+ /* Send all scheduled responses immediately */
+ while (s->jobs)
+ send_response_packet(s, s->jobs);
+}
--- /dev/null
+#ifndef fooresponseschedhfoo
+#define fooresponseschedhfoo
+
+/* $Id$ */
+
+/***
+ This file is part of avahi.
+
+ avahi is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ avahi is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+ Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with avahi; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+typedef struct AvahiResponseScheduler AvahiResponseScheduler;
+
+#include "iface.h"
+#include "address.h"
+
+AvahiResponseScheduler *avahi_response_scheduler_new(AvahiInterface *i);
+void avahi_response_scheduler_free(AvahiResponseScheduler *s);
+void avahi_response_scheduler_clear(AvahiResponseScheduler *s);
+void avahi_response_scheduler_force(AvahiResponseScheduler *s);
+
+gboolean avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, gboolean flush_cache, const AvahiAddress *querier, gboolean immediately);
+void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, gboolean flush_cache);
+void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier);
+
+#endif
}
static gint uint16_cmp(guint16 a, guint16 b) {
- return a == b ? 0 : (a < b ? a : b);
+ return a == b ? 0 : (a < b ? -1 : 1);
}
gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) {
gint r;
-/* gchar *t1, *t2; */
+ gchar *t1, *t2;
g_assert(a);
g_assert(b);
-/* 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); */
+ 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;
(r = uint16_cmp(a->data.srv.weight, b->data.srv.weight)) == 0 &&
(r = uint16_cmp(a->data.srv.port, b->data.srv.port)) == 0)
r = avahi_binary_domain_cmp(a->data.srv.name, b->data.srv.name);
+
+ g_message("SRV: %i", r);
return r;
}
}
}
+
+gboolean avahi_record_is_goodbye(AvahiRecord *r) {
+ g_assert(r);
+
+ return r->ttl == 0;
+}
gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b);
+gboolean avahi_record_is_goodbye(AvahiRecord *r);
+
#endif
typedef struct AvahiRecordListItem AvahiRecordListItem;
struct AvahiRecordListItem {
+ gboolean read;
AvahiRecord *record;
gboolean unicast_response;
gboolean flush_cache;
+ gboolean auxiliary;
AVAHI_LLIST_FIELDS(AvahiRecordListItem, items);
};
struct AvahiRecordList {
- AVAHI_LLIST_HEAD(AvahiRecordListItem, items);
+ AVAHI_LLIST_HEAD(AvahiRecordListItem, read);
+ AVAHI_LLIST_HEAD(AvahiRecordListItem, unread);
};
AvahiRecordList *avahi_record_list_new(void) {
AvahiRecordList *l = g_new(AvahiRecordList, 1);
- AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->items);
+ AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->read);
+ AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->unread);
return l;
}
}
static void item_free(AvahiRecordList *l, AvahiRecordListItem *i) {
+ g_assert(l);
g_assert(i);
- AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->items, i);
+ if (i->read)
+ AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->read, i);
+ else
+ AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i);
+
avahi_record_unref(i->record);
g_free(i);
}
void avahi_record_list_flush(AvahiRecordList *l) {
g_assert(l);
- while (l->items)
- item_free(l, l->items);
+ while (l->read)
+ item_free(l, l->read);
+ while (l->unread)
+ item_free(l, l->unread);
}
-AvahiRecord* avahi_record_list_pop(AvahiRecordList *l, gboolean *flush_cache, gboolean *unicast_response) {
+AvahiRecord* avahi_record_list_next(AvahiRecordList *l, gboolean *flush_cache, gboolean *unicast_response, gboolean *auxiliary) {
AvahiRecord *r;
+ AvahiRecordListItem *i;
- if (!l->items)
+ if (!(i = l->unread))
return NULL;
-
- r = avahi_record_ref(l->items->record);
- if (unicast_response) *unicast_response = l->items->unicast_response;
- if (flush_cache) *flush_cache = l->items->flush_cache;
- item_free(l, l->items);
+ g_assert(!i->read);
+
+ r = avahi_record_ref(i->record);
+ if (unicast_response)
+ *unicast_response = i->unicast_response;
+ if (flush_cache)
+ *flush_cache = i->flush_cache;
+ if (auxiliary)
+ *auxiliary = i->auxiliary;
+
+ AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i);
+ AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->read, i);
+
+ i->read = TRUE;
return r;
}
-void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, gboolean flush_cache, gboolean unicast_response) {
+static AvahiRecordListItem *get(AvahiRecordList *l, AvahiRecord *r) {
AvahiRecordListItem *i;
-
+
g_assert(l);
g_assert(r);
- for (i = l->items; i; i = i->items_next)
+ for (i = l->read; i; i = i->items_next)
if (avahi_record_equal_no_ttl(i->record, r))
- return;
+ return i;
+
+ for (i = l->unread; i; i = i->items_next)
+ if (avahi_record_equal_no_ttl(i->record, r))
+ return i;
+
+ return NULL;
+}
+
+void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, gboolean flush_cache, gboolean unicast_response, gboolean auxiliary) {
+ AvahiRecordListItem *i;
+
+ g_assert(l);
+ g_assert(r);
+
+ if (get(l, r))
+ return;
i = g_new(AvahiRecordListItem, 1);
i->unicast_response = unicast_response;
i->flush_cache = flush_cache;
+ i->auxiliary = auxiliary;
i->record = avahi_record_ref(r);
- AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->items, i);
+ i->read = FALSE;
+
+ AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->unread, i);
}
void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r) {
g_assert(l);
g_assert(r);
- for (i = l->items; i; i = i->items_next)
- if (avahi_record_equal_no_ttl(i->record, r)) {
- item_free(l, i);
- break;
- }
-}
+ if (!(i = get(l, r)))
+ return;
+ item_free(l, i);
+}
gboolean avahi_record_list_empty(AvahiRecordList *l) {
g_assert(l);
- return !l->items;
+ return !l->unread && !l->read;
}
void avahi_record_list_free(AvahiRecordList *l);
void avahi_record_list_flush(AvahiRecordList *l);
-AvahiRecord* avahi_record_list_pop(AvahiRecordList *l, gboolean *flush_cache, gboolean *unicast_response);
-void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, gboolean flush_cache, gboolean unicast_response);
+AvahiRecord* avahi_record_list_next(AvahiRecordList *l, gboolean *flush_cache, gboolean *unicast_response, gboolean *auxiliary);
+void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, gboolean flush_cache, gboolean unicast_response, gboolean auxiliary);
void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r);
gboolean avahi_record_list_empty(AvahiRecordList *l);
}
}
-static void add_aux_records(AvahiServer *s, AvahiInterface *i, const gchar *name, guint16 type, gboolean unicast_response) {
+static void enum_aux_records(AvahiServer *s, AvahiInterface *i, const gchar *name, guint16 type, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
AvahiKey *k;
+ AvahiEntry *e;
g_assert(s);
g_assert(i);
g_assert(name);
+ g_assert(callback);
+
+ g_assert(type != AVAHI_DNS_TYPE_ANY);
k = avahi_key_new(name, AVAHI_DNS_CLASS_IN, type);
- avahi_server_prepare_matching_responses(s, i, k, unicast_response);
+
+ 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))
+ callback(s, e->record, e->flags & AVAHI_ENTRY_UNIQUE, userdata);
+
avahi_key_unref(k);
}
-void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response) {
+void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata) {
g_assert(s);
g_assert(i);
- g_assert(e);
-
- /* Append a record to a packet and all the records referred by it */
-
- avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response);
+ g_assert(r);
+ g_assert(callback);
- if (e->record->key->class == AVAHI_DNS_CLASS_IN) {
- if (e->record->key->type == AVAHI_DNS_TYPE_PTR) {
- add_aux_records(s, i, e->record->data.ptr.name, AVAHI_DNS_TYPE_SRV, unicast_response);
- add_aux_records(s, i, e->record->data.ptr.name, AVAHI_DNS_TYPE_TXT, unicast_response);
- } else if (e->record->key->type == AVAHI_DNS_TYPE_SRV) {
- add_aux_records(s, i, e->record->data.srv.name, AVAHI_DNS_TYPE_A, unicast_response);
- add_aux_records(s, i, e->record->data.srv.name, AVAHI_DNS_TYPE_AAAA, unicast_response);
+ if (r->key->class == AVAHI_DNS_CLASS_IN) {
+ if (r->key->type == AVAHI_DNS_TYPE_PTR) {
+ enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_SRV, callback, userdata);
+ enum_aux_records(s, i, r->data.ptr.name, AVAHI_DNS_TYPE_TXT, callback, userdata);
+ } else if (r->key->type == AVAHI_DNS_TYPE_SRV) {
+ enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_A, callback, userdata);
+ enum_aux_records(s, i, r->data.srv.name, AVAHI_DNS_TYPE_AAAA, callback, userdata);
}
}
}
+void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary) {
+ g_assert(s);
+ g_assert(i);
+ g_assert(e);
+
+ avahi_record_list_push(s->record_list, e->record, e->flags & AVAHI_ENTRY_UNIQUE, unicast_response, auxiliary);
+}
+
void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, AvahiKey *k, gboolean unicast_response) {
AvahiEntry *e;
gchar *txt;
for (e = s->entries; e; e = e->entries_next)
if (!e->dead && avahi_key_pattern_match(k, e->record->key) && avahi_entry_registered(s, e, i))
- avahi_server_prepare_response(s, i, e, unicast_response);
+ avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
} 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))
- avahi_server_prepare_response(s, i, e, unicast_response);
+ avahi_server_prepare_response(s, i, e, unicast_response, FALSE);
}
}
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);
+ avahi_interface_post_response(i, e->record, FALSE, NULL, TRUE);
valid = FALSE;
}
return valid;
}
+static void append_aux_callback(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata) {
+ gboolean *unicast_response = userdata;
+
+ g_assert(s);
+ g_assert(r);
+ g_assert(unicast_response);
+
+ avahi_record_list_push(s->record_list, r, flush_cache, *unicast_response, TRUE);
+}
+
+static void append_aux_records_to_list(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, gboolean unicast_response) {
+ g_assert(s);
+ g_assert(r);
+
+ avahi_server_enumerate_aux_records(s, i, r, append_aux_callback, &unicast_response);
+}
+
void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
g_assert(s);
reply = avahi_dns_packet_new_reply(p, 512 /* unicast DNS maximum packet size is 512 */ , TRUE, TRUE);
- while ((r = avahi_record_list_pop(s->record_list, NULL, NULL))) {
+ while ((r = avahi_record_list_next(s->record_list, NULL, NULL, NULL))) {
+
+ append_aux_records_to_list(s, i, r, FALSE);
if (avahi_dns_packet_append_record(reply, r, FALSE, 10))
avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
avahi_dns_packet_free(reply);
} else {
- gboolean unicast_response, flush_cache;
+ gboolean unicast_response, flush_cache, auxiliary;
AvahiDnsPacket *reply = NULL;
AvahiRecord *r;
- while ((r = avahi_record_list_pop(s->record_list, &flush_cache, &unicast_response))) {
+ while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
- if (!avahi_interface_post_response(i, r, flush_cache, FALSE, a) && unicast_response) {
-
+
+ if (!avahi_interface_post_response(i, r, flush_cache, a, flush_cache && !auxiliary) && unicast_response) {
+
+ append_aux_records_to_list(s, i, r, unicast_response);
+
/* Due to some reasons the record has not been scheduled.
* The client requested an unicast response in that
* case. Therefore we prepare such a response */
}
if (avahi_dns_packet_get_field(reply, AVAHI_DNS_FIELD_ANCOUNT) == 0) {
- gchar *t = avahi_record_to_string(r);
- g_warning("Record [%s] too large, doesn't fit in any packet!", t);
- g_free(t);
- break;
+ guint size;
+
+ /* The record is too large for one packet, so create a larger packet */
+
+ avahi_dns_packet_free(reply);
+ size = avahi_record_get_estimate_size(r) + AVAHI_DNS_PACKET_HEADER_SIZE;
+ if (size > AVAHI_DNS_PACKET_MAX_SIZE)
+ size = AVAHI_DNS_PACKET_MAX_SIZE;
+ reply = avahi_dns_packet_new_reply(p, size, FALSE, TRUE);
+
+ if (!avahi_dns_packet_append_record(reply, r, flush_cache, 0)) {
+ avahi_dns_packet_free(reply);
+
+ gchar *t = avahi_record_to_string(r);
+ g_warning("Record [%s] too large, doesn't fit in any packet!", t);
+ g_free(t);
+ break;
+ } else
+ avahi_dns_packet_inc_field(reply, AVAHI_DNS_FIELD_ANCOUNT);
}
/* Appending the record didn't succeeed, so let's send this packet, and create a new one */
avahi_dns_packet_free(reply);
}
}
+
+ avahi_record_list_flush(s->record_list);
}
static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, const AvahiAddress *a, guint16 port, gboolean legacy_unicast) {
if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) {
g_warning("Packet too short (1)");
goto fail;
- }
+ }
- avahi_packet_scheduler_incoming_query(i->scheduler, key);
+ avahi_query_scheduler_incoming(i->query_scheduler, key);
avahi_server_prepare_matching_responses(s, i, key, unicast_response);
avahi_key_unref(key);
}
}
if (handle_conflict(s, i, record, unique, a)) {
- avahi_packet_scheduler_incoming_known_answer(i->scheduler, record, a);
+ avahi_response_scheduler_suppress(i->response_scheduler, record, a);
avahi_record_list_drop(s->record_list, record);
}
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, cache_flush);
+ avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush);
}
}
gboolean ignore_bad_ttl;
- AvahiRecordList *record_list; /* Used for assembling responses */
+ /* Used for assembling responses */
+ AvahiRecordList *record_list;
};
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_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response);
+void avahi_server_prepare_response(AvahiServer *s, AvahiInterface *i, AvahiEntry *e, gboolean unicast_response, gboolean auxiliary);
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);
gboolean avahi_entry_commited(AvahiEntry *e);
+void avahi_server_enumerate_aux_records(AvahiServer *s, AvahiInterface *i, AvahiRecord *r, void (*callback)(AvahiServer *s, AvahiRecord *r, gboolean flush_cache, gpointer userdata), gpointer userdata);
+
#endif
return tv;
}
-gint avahi_age(const GTimeVal *a) {
+glong avahi_age(const GTimeVal *a) {
GTimeVal now;
g_assert(a);
GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter);
-gint avahi_age(const GTimeVal *a);
+glong avahi_age(const GTimeVal *a);
gboolean avahi_domain_equal(const gchar *a, const gchar *b);
gint avahi_binary_domain_cmp(const gchar *a, const gchar *b);
todo:
* Add some APIs to get the clean service name from RR for browsing
-RFC MUSTs:
- * one RR too large for single packet
-
-* response job dependencies
-
* test against apple test suite
* release!
* either send entire RRSET or don't set flush cache bit!
* mantain flush cache bit correctly in psched
* Return to probing state on conflict
+* response job dependencies
+* enlarge packet in case a record/query is too large to fit in a normal packet