From: Lennart Poettering Date: Tue, 17 May 2005 00:03:50 +0000 (+0000) Subject: * split packet scheduler into three seperate parts X-Git-Url: https://git.meshlink.io/?a=commitdiff_plain;h=28d336020ca1f6dbb88d64cac3ffdd1a67ee3de7;p=catta * split packet scheduler into three seperate parts * test against Aplle test suit, most tests pass now * suppress auxiliary records by known answers * handle very large records git-svn-id: file:///home/lennart/svn/public/avahi/trunk@74 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- diff --git a/avahi-core/Makefile.am b/avahi-core/Makefile.am index cd22097..7554d24 100644 --- a/avahi-core/Makefile.am +++ b/avahi-core/Makefile.am @@ -35,7 +35,8 @@ noinst_PROGRAMS = \ prioq-test \ strlst-test \ avahi-test \ - alternative-test + alternative-test \ + conformance-test libavahi_core_la_SOURCES = \ timeeventq.c timeeventq.h\ @@ -49,7 +50,9 @@ libavahi_core_la_SOURCES = \ 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 \ @@ -85,7 +88,6 @@ dns_test_LDADD = $(AM_LDADD) avahi_test_SOURCES = \ avahi-test.c \ $(libavahi_core_la_SOURCES) - avahi_test_CFLAGS = $(AM_CFLAGS) avahi_test_LDADD = $(AM_LDADD) @@ -95,6 +97,12 @@ alternative_test_SOURCES = \ 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 diff --git a/avahi-core/announce.c b/avahi-core/announce.c index 8149b38..ddcfd39 100644 --- a/avahi-core/announce.c +++ b/avahi-core/announce.c @@ -156,7 +156,7 @@ static void next_state(AvahiAnnouncement *a) { /* 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); @@ -399,7 +399,7 @@ static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, g return; g = make_goodbye_record(e->record); - avahi_interface_post_response(i, g, e->flags & AVAHI_ENTRY_UNIQUE, TRUE, NULL); + avahi_interface_post_response(i, g, e->flags & AVAHI_ENTRY_UNIQUE, NULL, TRUE); avahi_record_unref(g); } diff --git a/avahi-core/conformance-test.c b/avahi-core/conformance-test.c new file mode 100644 index 0000000..001bff1 --- /dev/null +++ b/avahi-core/conformance-test.c @@ -0,0 +1,108 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#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; +} diff --git a/avahi-core/iface.c b/avahi-core/iface.c index 7f51305..2448678 100644 --- a/avahi-core/iface.c +++ b/avahi-core/iface.c @@ -93,8 +93,8 @@ static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, gboolean 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); @@ -102,7 +102,9 @@ static void free_interface(AvahiInterfaceMonitor *m, AvahiInterface *i, gboolean 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); @@ -175,7 +177,9 @@ static void new_interface(AvahiInterfaceMonitor *m, AvahiHwInterface *hw, guchar 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); @@ -208,6 +212,9 @@ static void check_interface_relevant(AvahiInterfaceMonitor *m, AvahiInterface *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; @@ -506,17 +513,17 @@ gboolean avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, gboolean i 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; } @@ -526,7 +533,7 @@ gboolean avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *record, gboo 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; } diff --git a/avahi-core/iface.h b/avahi-core/iface.h index e229b2c..ab75a18 100644 --- a/avahi-core/iface.h +++ b/avahi-core/iface.h @@ -34,7 +34,9 @@ typedef struct AvahiHwInterface AvahiHwInterface; #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" @@ -77,7 +79,9 @@ struct AvahiInterface { 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); @@ -107,8 +111,8 @@ void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p); 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); diff --git a/avahi-core/probe-sched.c b/avahi-core/probe-sched.c new file mode 100644 index 0000000..9cea3e0 --- /dev/null +++ b/avahi-core/probe-sched.c @@ -0,0 +1,273 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "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; +} diff --git a/avahi-core/probe-sched.h b/avahi-core/probe-sched.h new file mode 100644 index 0000000..07933a7 --- /dev/null +++ b/avahi-core/probe-sched.h @@ -0,0 +1,36 @@ +#ifndef fooprobeschedhfoo +#define fooprobeschedhfoo + +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiProbeScheduler AvahiProbeScheduler; + +#include "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 diff --git a/avahi-core/psched.c b/avahi-core/psched.c deleted file mode 100644 index 5121054..0000000 --- a/avahi-core/psched.c +++ /dev/null @@ -1,738 +0,0 @@ -/* $Id$ */ - -/*** - This file is part of avahi. - - avahi is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - avahi is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General - Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with avahi; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - USA. -***/ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include "util.h" -#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; -} diff --git a/avahi-core/psched.h b/avahi-core/psched.h deleted file mode 100644 index 4b9e5b2..0000000 --- a/avahi-core/psched.h +++ /dev/null @@ -1,101 +0,0 @@ -#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 diff --git a/avahi-core/query-sched.c b/avahi-core/query-sched.c new file mode 100644 index 0000000..32d0e91 --- /dev/null +++ b/avahi-core/query-sched.c @@ -0,0 +1,379 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "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); +} + diff --git a/avahi-core/query-sched.h b/avahi-core/query-sched.h new file mode 100644 index 0000000..b77363c --- /dev/null +++ b/avahi-core/query-sched.h @@ -0,0 +1,37 @@ +#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 diff --git a/avahi-core/response-sched.c b/avahi-core/response-sched.c new file mode 100644 index 0000000..c4218eb --- /dev/null +++ b/avahi-core/response-sched.c @@ -0,0 +1,482 @@ +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "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); +} diff --git a/avahi-core/response-sched.h b/avahi-core/response-sched.h new file mode 100644 index 0000000..10fd821 --- /dev/null +++ b/avahi-core/response-sched.h @@ -0,0 +1,39 @@ +#ifndef fooresponseschedhfoo +#define fooresponseschedhfoo + +/* $Id$ */ + +/*** + This file is part of avahi. + + avahi is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + avahi is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General + Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with avahi; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +typedef struct AvahiResponseScheduler AvahiResponseScheduler; + +#include "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 diff --git a/avahi-core/rr.c b/avahi-core/rr.c index 5b5ec7e..a21595f 100644 --- a/avahi-core/rr.c +++ b/avahi-core/rr.c @@ -452,21 +452,21 @@ static gint lexicographical_memcmp(gconstpointer a, size_t al, gconstpointer b, } 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; @@ -486,6 +486,8 @@ gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { (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; } @@ -529,3 +531,9 @@ gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b) { } } + +gboolean avahi_record_is_goodbye(AvahiRecord *r) { + g_assert(r); + + return r->ttl == 0; +} diff --git a/avahi-core/rr.h b/avahi-core/rr.h index ef267f2..c620695 100644 --- a/avahi-core/rr.h +++ b/avahi-core/rr.h @@ -135,4 +135,6 @@ guint avahi_record_get_estimate_size(AvahiRecord *r); gint avahi_record_lexicographical_compare(AvahiRecord *a, AvahiRecord *b); +gboolean avahi_record_is_goodbye(AvahiRecord *r); + #endif diff --git a/avahi-core/rrlist.c b/avahi-core/rrlist.c index 429f142..51e700c 100644 --- a/avahi-core/rrlist.c +++ b/avahi-core/rrlist.c @@ -25,20 +25,24 @@ 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; } @@ -50,9 +54,14 @@ void avahi_record_list_free(AvahiRecordList *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); } @@ -60,40 +69,71 @@ static void item_free(AvahiRecordList *l, AvahiRecordListItem *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) { @@ -102,16 +142,14 @@ 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; } diff --git a/avahi-core/rrlist.h b/avahi-core/rrlist.h index 0578417..9651475 100644 --- a/avahi-core/rrlist.h +++ b/avahi-core/rrlist.h @@ -31,8 +31,8 @@ AvahiRecordList *avahi_record_list_new(void); 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); diff --git a/avahi-core/server.c b/avahi-core/server.c index 6366dd8..31cbecc 100644 --- a/avahi-core/server.c +++ b/avahi-core/server.c @@ -102,38 +102,51 @@ static void cleanup_dead(AvahiServer *s) { } } -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; @@ -151,7 +164,7 @@ void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, 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 { @@ -159,7 +172,7 @@ void avahi_server_prepare_matching_responses(AvahiServer *s, AvahiInterface *i, 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); } } @@ -264,7 +277,7 @@ static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord * 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; } @@ -312,6 +325,23 @@ static gboolean handle_conflict(AvahiServer *s, AvahiInterface *i, AvahiRecord * 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); @@ -324,7 +354,9 @@ void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsP 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); @@ -343,14 +375,17 @@ void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsP 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 */ @@ -373,10 +408,25 @@ void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsP } 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 */ @@ -395,6 +445,8 @@ void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsP 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) { @@ -417,9 +469,9 @@ static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, c if (!(key = avahi_dns_packet_consume_key(p, &unicast_response))) { g_warning("Packet too short (1)"); goto fail; - } + } - avahi_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); } @@ -435,7 +487,7 @@ static void handle_query(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i, c } 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); } @@ -496,7 +548,7 @@ static void handle_response(AvahiServer *s, AvahiDnsPacket *p, AvahiInterface *i if (handle_conflict(s, i, record, cache_flush, a)) { avahi_cache_update(i->cache, record, cache_flush, a); - avahi_packet_scheduler_incoming_response(i->scheduler, record, cache_flush); + avahi_response_scheduler_incoming(i->response_scheduler, record, cache_flush); } } diff --git a/avahi-core/server.h b/avahi-core/server.h index 420c501..7283b79 100644 --- a/avahi-core/server.h +++ b/avahi-core/server.h @@ -89,14 +89,15 @@ struct AvahiServer { 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); @@ -104,4 +105,6 @@ void avahi_entry_group_change_state(AvahiEntryGroup *g, AvahiEntryGroupState sta 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 diff --git a/avahi-core/util.c b/avahi-core/util.c index 9b84d12..fa5eb71 100644 --- a/avahi-core/util.c +++ b/avahi-core/util.c @@ -191,7 +191,7 @@ GTimeVal *avahi_elapse_time(GTimeVal *tv, guint msec, guint jitter) { return tv; } -gint avahi_age(const GTimeVal *a) { +glong avahi_age(const GTimeVal *a) { GTimeVal now; g_assert(a); diff --git a/avahi-core/util.h b/avahi-core/util.h index 59a0be6..04dd425 100644 --- a/avahi-core/util.h +++ b/avahi-core/util.h @@ -36,7 +36,7 @@ gint avahi_wait_for_write(gint fd); 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); diff --git a/todo b/todo index 108c20a..fa31683 100644 --- a/todo +++ b/todo @@ -1,11 +1,6 @@ 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! @@ -41,3 +36,5 @@ done: * 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