]> git.meshlink.io Git - catta/commitdiff
* split packet scheduler into three seperate parts
authorLennart Poettering <lennart@poettering.net>
Tue, 17 May 2005 00:03:50 +0000 (00:03 +0000)
committerLennart Poettering <lennart@poettering.net>
Tue, 17 May 2005 00:03:50 +0000 (00:03 +0000)
* 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

22 files changed:
avahi-core/Makefile.am
avahi-core/announce.c
avahi-core/conformance-test.c [new file with mode: 0644]
avahi-core/iface.c
avahi-core/iface.h
avahi-core/probe-sched.c [new file with mode: 0644]
avahi-core/probe-sched.h [new file with mode: 0644]
avahi-core/psched.c [deleted file]
avahi-core/psched.h [deleted file]
avahi-core/query-sched.c [new file with mode: 0644]
avahi-core/query-sched.h [new file with mode: 0644]
avahi-core/response-sched.c [new file with mode: 0644]
avahi-core/response-sched.h [new file with mode: 0644]
avahi-core/rr.c
avahi-core/rr.h
avahi-core/rrlist.c
avahi-core/rrlist.h
avahi-core/server.c
avahi-core/server.h
avahi-core/util.c
avahi-core/util.h
todo

index cd22097e27ca417556fdef3b61adefbb7f4896eb..7554d24317e87f1c815ee16c2269687fe0f3c26d 100644 (file)
@@ -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
 
index 8149b387a3464356a0e50cc32394df831bb4fe7c..ddcfd39d5347300baa14540a89d7674cd7b6a9ba 100644 (file)
@@ -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 (file)
index 0000000..001bff1
--- /dev/null
@@ -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 <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;
+}
index 7f51305b54a0de895ab5e4afaddfe5987df2ac82..2448678f8b3e87243b93ac0216fd673d4ab4a333 100644 (file)
@@ -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;
 }
index e229b2c0a16fc51f2e1f127af8f22144793aa067..ab75a183aa922b3cd6ad8c1a2f47ac303b0c023e 100644 (file)
@@ -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 (file)
index 0000000..9cea3e0
--- /dev/null
@@ -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 <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;
+}
diff --git a/avahi-core/probe-sched.h b/avahi-core/probe-sched.h
new file mode 100644 (file)
index 0000000..07933a7
--- /dev/null
@@ -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 (file)
index 5121054..0000000
+++ /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 <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;
-}
diff --git a/avahi-core/psched.h b/avahi-core/psched.h
deleted file mode 100644 (file)
index 4b9e5b2..0000000
+++ /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 (file)
index 0000000..32d0e91
--- /dev/null
@@ -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 <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);
+}
+
diff --git a/avahi-core/query-sched.h b/avahi-core/query-sched.h
new file mode 100644 (file)
index 0000000..b77363c
--- /dev/null
@@ -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 (file)
index 0000000..c4218eb
--- /dev/null
@@ -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 <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);
+}
diff --git a/avahi-core/response-sched.h b/avahi-core/response-sched.h
new file mode 100644 (file)
index 0000000..10fd821
--- /dev/null
@@ -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
index 5b5ec7ec8d45d3836b34a2eaf35949c9683eb54e..a21595fd3eab135678cbece603c809ec5c753fa0 100644 (file)
@@ -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;
+}
index ef267f2f4026e860d6aaa249a9f84b731552b9a1..c62069536f2a2069ec0773868da5cdf42dcd5954 100644 (file)
@@ -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
index 429f142c1fa64f5ff430431dd5198cc56bbf085f..51e700c760fa610fe1ac6d2bdc285d6cb0be00c9 100644 (file)
 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;
 }
index 057841767383be16c7a3e7d5436a9335ebf03046..9651475fecb124d69986424559b1dcfcd2ecb780 100644 (file)
@@ -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);
index 6366dd835ebd04437654b16c9086978803d128a6..31cbecce30077599043bac6ccda17020f9c044a1 100644 (file)
@@ -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);
             }
         }
             
index 420c50158c115fbedf64f250f121adc44f740be8..7283b79ad2e452febf72dd85d01340012fba549d 100644 (file)
@@ -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
index 9b84d12134d104f3d1730dcfd6bb3db7bb1631bc..fa5eb71f287eb26e10287cbde0fb05f5a88fd194 100644 (file)
@@ -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);
index 59a0be6573c0d7a074755549f48f766a515b2e92..04dd4258716cd7969d8799e6284c91094c898d61 100644 (file)
@@ -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 108c20aac947129b25bbb7b8b9aaeea0a209fda3..fa31683b0a20580cd1e906614aec0575c3153c51 100644 (file)
--- 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