]> git.meshlink.io Git - catta/commitdiff
* Make "NameAcquired" warning line disappear in avahi-client
authorLennart Poettering <lennart@poettering.net>
Sun, 13 Nov 2005 16:36:33 +0000 (16:36 +0000)
committerLennart Poettering <lennart@poettering.net>
Sun, 13 Nov 2005 16:36:33 +0000 (16:36 +0000)
Scheduler tweaks:

* Add some more comments

* Remove scheduled queries from the query queue if the querier which issued
  them dies. This reduces traffic immensly when many short lived queries are
  made, e.g. during host name lookups.

* Don't free a querier object immediately when it is no longer referenced.
  Instead keep it and try to recycle it in case someone else wants to do the
  same query later on. Free it at the latest moment possible: just before the
  next query is scheduled to be made. This reduces traffic immensly when many
  short lived queries are made.

  With these two changes we can minimize the traffic to zero or near zero for
  many simple lookups.

* When responding records with the FLUSH_CACHE bit set, reply immediately only
  when all response record have this bit set. Prior to this change we replied
  imediately as soon as one record hat this bit set. This change should make us
  pass *all* Bonjour mDNS conformance tests without any exceptions.

git-svn-id: file:///home/lennart/svn/public/avahi/trunk@954 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe

12 files changed:
avahi-client/client.c
avahi-core/cache.c
avahi-core/iface.c
avahi-core/iface.h
avahi-core/multicast-lookup.c
avahi-core/querier.c
avahi-core/querier.h
avahi-core/query-sched.c
avahi-core/query-sched.h
avahi-core/rrlist.c
avahi-core/rrlist.h
avahi-core/server.c

index 1067456ed81104fb7f3d66f3d9aa80ee9ceb9ced..2fd5123aac4f933f796fac6ea19d18f0f4897673 100644 (file)
@@ -115,7 +115,11 @@ static DBusHandlerResult filter_func(DBusConnection *bus, DBusMessage *message,
         avahi_client_set_errno(client, AVAHI_ERR_DISCONNECTED);
         goto fail;
 
-    } if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+    } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameAcquired")) {
+
+        /* Ignore this message */
+        
+    } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
         char *name, *old, *new;
         
         if (!dbus_message_get_args(
@@ -154,7 +158,7 @@ static DBusHandlerResult filter_func(DBusConnection *bus, DBusMessage *message,
 
     } else if (!avahi_client_is_connected(client)) {
         
-        /* Ignore messages, we get in unconnected state */
+        /* Ignore messages we get in unconnected state */
         
     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVER, "StateChanged")) {
         int32_t state;
index 5e87706947323d8832cd275f813ed56d8de41dfa..c26b6bb9d81225e592faf0b370b7cd877eb21a79 100644 (file)
@@ -211,11 +211,9 @@ static void elapse_func(AvahiTimeEvent *t, void *userdata) {
 
         assert(percent > 0);
 
-        /* Request a cache update, if we are subscribed to this entry */
-        if (avahi_querier_exists(e->cache->interface, e->record->key)) {
-/*             avahi_log_debug("Requesting cache entry update at %i%% for %s.", percent, txt);   */
-            avahi_interface_post_query(e->cache->interface, e->record->key, 1);
-        }
+        /* Request a cache update if we are subscribed to this entry */
+        if (avahi_querier_shall_refresh_cache(e->cache->interface, e->record->key))
+            avahi_interface_post_query(e->cache->interface, e->record->key, 0, NULL);
         
         /* Check again later */
         next_expiry(e->cache, e, percent);
index 0e3ef686f99d6a9fdfd99d6ec0eeadcc8785c6d6..b466f48838e3ad37fee7996a8b892184e1239c89 100644 (file)
@@ -583,16 +583,21 @@ void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p) {
     avahi_interface_send_packet_unicast(i, p, NULL, 0);
 }
 
-int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately) {
+int avahi_interface_post_query(AvahiInterface *i, AvahiKey *key, int immediately, unsigned *ret_id) {
     assert(i);
     assert(key);
 
     if (avahi_interface_is_relevant(i))
-        return avahi_query_scheduler_post(i->query_scheduler, key, immediately);
+        return avahi_query_scheduler_post(i->query_scheduler, key, immediately, ret_id);
 
     return 0;
 }
 
+int avahi_interface_withraw_query(AvahiInterface *i, unsigned id) {
+
+    return avahi_query_scheduler_withdraw_by_id(i->query_scheduler, id);
+}
+
 int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) {
     assert(i);
     assert(record);
index 30d3d229d122d70bc82a7cc1322d8ab88076beb4..4106ea7b717739096d9c8fd911af437f1412d56b 100644 (file)
@@ -166,7 +166,8 @@ int avahi_interface_is_relevant(AvahiInterface *i);
 void avahi_interface_send_packet(AvahiInterface *i, AvahiDnsPacket *p);
 void avahi_interface_send_packet_unicast(AvahiInterface *i, AvahiDnsPacket *p, const AvahiAddress *a, uint16_t port);
 
-int avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, int immediately);
+int avahi_interface_post_query(AvahiInterface *i, AvahiKey *k, int immediately, unsigned *ret_id);
+int avahi_interface_withraw_query(AvahiInterface *i, unsigned id);
 int avahi_interface_post_response(AvahiInterface *i, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately);
 int avahi_interface_post_probe(AvahiInterface *i, AvahiRecord *p, int immediately);
 
index fa8c85951a5d0ba06289d3ff8c63a3af017ed658..8101df5cd987aedc85efdbcf00199d4a833a06f4 100644 (file)
@@ -114,9 +114,10 @@ AvahiMulticastLookup *avahi_multicast_lookup_new(
     avahi_querier_add_for_all(e->server, interface, protocol, l->key, &tv);
     l->queriers_added = 1;
 
-    /* add a second */
+    /* Add a second */
     avahi_timeval_add(&tv, 1000000);
 
+    /* Issue the ALL_FOR_NOW event one second after the querier was initially created */
     l->all_for_now_event = avahi_time_event_new(e->server->time_event_queue, &tv, all_for_now_callback, l);
     
     return l;
index 41acc24bf007c157d23316ceba9697346efd6f7a..b1069d1846ef45a79fb19eb0d7b8e537552c9453 100644 (file)
@@ -42,6 +42,9 @@ struct AvahiQuerier {
     AvahiTimeEvent *time_event;
 
     struct timeval creation_time;
+
+    unsigned post_id;
+    int post_id_valid;
     
     AVAHI_LLIST_FIELDS(AvahiQuerier, queriers);
 };
@@ -64,7 +67,24 @@ static void querier_elapse_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *us
     
     assert(q);
 
-    avahi_interface_post_query(q->interface, q->key, 0);
+    if (q->n_used <= 0) {
+
+        /* We are not referenced by anyone anymore, so let's free
+         * ourselves. We should not send out any further queries from
+         * this querier object anymore. */
+
+        avahi_querier_free(q);
+        return;
+    }
+
+    if (avahi_interface_post_query(q->interface, q->key, 0, &q->post_id)) {
+
+        /* The queue accepted our query. We store the query id here,
+         * that allows us to drop the query at a later point if the
+         * query is very short-lived. */
+        
+        q->post_id_valid = 1;
+    }
 
     q->sec_delay *= 2;
     
@@ -83,10 +103,13 @@ void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_cti
     assert(key);
     
     if ((q = avahi_hashmap_lookup(i->queriers_by_key, key))) {
+        
         /* Someone is already browsing for records of this RR key */
         q->n_used++;
 
-        /* Return the creation time */
+        /* Return the creation time. This is used for generating the
+         * ALL_FOR_NOW event one second after the querier was
+         * initially created. */
         if (ret_ctime)
             *ret_ctime = q->creation_time;
         return;
@@ -100,10 +123,12 @@ void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_cti
     q->interface = i;
     q->n_used = 1;
     q->sec_delay = 1;
+    q->post_id_valid = 0;
     gettimeofday(&q->creation_time, NULL);
 
     /* Do the initial query */
-    avahi_interface_post_query(i, key, 0);
+    if (avahi_interface_post_query(i, key, 0, &q->post_id))
+        q->post_id_valid = 1;
 
     /* Schedule next queries */
     q->time_event = avahi_time_event_new(i->monitor->server->time_event_queue, avahi_elapse_time(&tv, q->sec_delay*1000, 0), querier_elapse_callback, q);
@@ -111,7 +136,9 @@ void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_cti
     AVAHI_LLIST_PREPEND(AvahiQuerier, queriers, i->queriers, q);
     avahi_hashmap_insert(i->queriers_by_key, q->key, q);
 
-    /* Return the creation time */
+    /* Return the creation time. This is used for generating the
+     * ALL_FOR_NOW event one second after the querier was initially
+     * created. */
     if (ret_ctime)
         *ret_ctime = q->creation_time;
 }
@@ -119,16 +146,29 @@ void avahi_querier_add(AvahiInterface *i, AvahiKey *key, struct timeval *ret_cti
 void avahi_querier_remove(AvahiInterface *i, AvahiKey *key) {
     AvahiQuerier *q;
 
-    if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key))) {
-        /* The was no querier for this RR key */
-        avahi_log_warn(__FILE__": querier_remove() called but no querier to remove");
+    if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)) || q->n_used <= 0) {
+        /* There was no querier for this RR key, or it wasn't referenced by anyone */
+        avahi_log_warn(__FILE__": querier_remove() called but no querier to remove.");
         return;
     }
 
-    assert(q->n_used >= 1);
+    if ((--q->n_used) <= 0) {
 
-    if ((--q->n_used) <= 0)
-        avahi_querier_free(q);
+        /* Nobody references us anymore. */
+
+        if (q->post_id_valid && avahi_interface_withraw_query(i, q->post_id)) {
+
+            /* We succeeded in withdrawing our query from the queue,
+             * so let's drop dead. */
+
+            avahi_querier_free(q);
+        }
+
+        /* If we failed to withdraw our query from the queue, we stay
+         * alive, in case someone else might recycle our querier at a
+         * later point. We are freed at our next expiry, in case
+         * nobody recycled us. */
+    }
 }
 
 static void remove_querier_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
@@ -183,14 +223,42 @@ void avahi_querier_add_for_all(AvahiServer *s, AvahiIfIndex idx, AvahiProtocol p
     avahi_interface_monitor_walk(s->monitor, idx, protocol, add_querier_callback, &cbdata);
 }
 
-int avahi_querier_exists(AvahiInterface *i, AvahiKey *key) {
+int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key) {
+    AvahiQuerier *q;
+    
     assert(i);
     assert(key);
 
-    if (avahi_hashmap_lookup(i->queriers_by_key, key))
-        return 1;
+    /* Called by the cache maintainer */
 
-    return 0;
+    if (!(q = avahi_hashmap_lookup(i->queriers_by_key, key)))
+        /* This key is currently not subscribed at all, so no cache
+         * refresh is needed */
+        return 0;
+    
+    if (q->n_used <= 0) {
+
+        /* If this is an entry nobody references right now, don't
+         * consider it "existing". */
+        
+        /* Remove this querier since it is referenced by nobody
+         * and the cached data will soon be out of date */
+        avahi_querier_free(q);
+
+        /* Tell the cache that no refresh is needed */
+        return 0;
+        
+    } else {
+        struct timeval tv;
+
+        /* We can defer our query a little, since the cache will now
+         * issue a refresh query anyway. */
+        avahi_elapse_time(&tv, q->sec_delay*1000, 0);
+        avahi_time_event_update(q->time_event, &tv);
+
+        /* Tell the cache that a refresh should be issued */
+        return 1;
+    }
 }
 
 void avahi_querier_free_all(AvahiInterface *i) {
index 3ec4564d6ab3c47ddc42ecc7423a7c20c9c5ad8d..3f4eead8ebcc87a75a682951928b0dd5db259e0b 100644 (file)
@@ -45,6 +45,6 @@ void avahi_querier_free(AvahiQuerier *q);
 void avahi_querier_free_all(AvahiInterface *i);
 
 /** Return 1 if there is a querier for the specified key on the specified interface */
-int avahi_querier_exists(AvahiInterface *i, AvahiKey *key);
+int avahi_querier_shall_refresh_cache(AvahiInterface *i, AvahiKey *key);
 
 #endif
index 00968385b37dda144b93dc07ff14437345076243..f875411187ee5303df6257cc0d683673219f5348 100644 (file)
@@ -36,6 +36,9 @@ typedef struct AvahiQueryJob AvahiQueryJob;
 typedef struct AvahiKnownAnswer AvahiKnownAnswer;
 
 struct AvahiQueryJob {
+    unsigned id;
+    int n_posted;
+    
     AvahiQueryScheduler *scheduler;
     AvahiTimeEvent *time_event;
     
@@ -44,6 +47,14 @@ struct AvahiQueryJob {
 
     AvahiKey *key;
 
+    /* Jobs are stored in a simple linked list. It might turn out in
+     * the future that this list grows too long and we must switch to
+     * some other kind of data structure. This needs further
+     * investigation. I expect the list to be very short (< 20
+     * entries) most of the time, but this might be a wrong
+     * assumption, especially on setups where traffic reflection is
+     * involved. */
+    
     AVAHI_LLIST_FIELDS(AvahiQueryJob, jobs);
 };
 
@@ -58,6 +69,8 @@ struct AvahiQueryScheduler {
     AvahiInterface *interface;
     AvahiTimeEventQueue *time_event_queue;
 
+    unsigned next_id;
+
     AVAHI_LLIST_HEAD(AvahiQueryJob, jobs);
     AVAHI_LLIST_HEAD(AvahiQueryJob, history);
     AVAHI_LLIST_HEAD(AvahiKnownAnswer, known_answers);
@@ -77,6 +90,8 @@ static AvahiQueryJob* job_new(AvahiQueryScheduler *s, AvahiKey *key, int done) {
     qj->scheduler = s;
     qj->key = avahi_key_ref(key);
     qj->time_event = NULL;
+    qj->n_posted = 1;
+    qj->id = s->next_id++;
     
     if ((qj->done = done)) 
         AVAHI_LLIST_PREPEND(AvahiQueryJob, jobs, s->history, qj);
@@ -144,6 +159,7 @@ AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i) {
     
     s->interface = i;
     s->time_event_queue = i->monitor->server->time_event_queue;
+    s->next_id = 0;
     
     AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->jobs);
     AVAHI_LLIST_HEAD_INIT(AvahiQueryJob, s->history);
@@ -335,25 +351,21 @@ static AvahiQueryJob* find_history_job(AvahiQueryScheduler *s, AvahiKey *key) {
     return NULL;
 }
 
-int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately) {
+int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id) {
     struct timeval tv;
     AvahiQueryJob *qj;
     
     assert(s);
     assert(key);
 
-    if ((qj = find_history_job(s, key))) {
-/*         avahi_log_debug("Query suppressed by local duplicate suppression (history)"); */
+    if ((qj = find_history_job(s, key)))
         return 0;
-    }
     
     avahi_elapse_time(&tv, immediately ? 0 : AVAHI_QUERY_DEFER_MSEC, 0);
 
     if ((qj = find_scheduled_job(s, key))) {
         /* Duplicate questions suppression */
 
-/*         avahi_log_debug("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 */
@@ -361,18 +373,21 @@ int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immedi
             avahi_time_event_update(qj->time_event, &qj->delivery);
         }
 
-        return 1;
+        qj->n_posted++;
+        
     } else {
-/*         avahi_log_debug("Accepted new query job."); */
 
         if (!(qj = job_new(s, key, 0)))
             return 0; /* OOM */
         
         qj->delivery = tv;
         qj->time_event = avahi_time_event_new(s->time_event_queue, &qj->delivery, elapse_callback, qj);
-        
-        return 1;
     }
+
+    if (ret_id)
+        *ret_id = qj->id;
+    
+    return 1;
 }
 
 void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) {
@@ -382,19 +397,54 @@ void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key) {
     assert(key);
 
     /* This function is called whenever an incoming query was
-     * receieved. We drop scheduled queries that match. The keyword is
+     * received. We drop scheduled queries that match. The keyword is
      * "DUPLICATE QUESTION SUPPRESION". */
 
     if ((qj = find_scheduled_job(s, key))) {
-/*         avahi_log_debug("Query suppressed by distributed duplicate suppression"); */
         job_mark_done(s, qj);
         return;
     }
-    
-    if (!(qj = job_new(s, key, 1)))
-        return; /* OOM */
+
+    /* Look if there's a history job for this key. If there is, just
+     * update the elapse time */
+    if (!(qj = find_history_job(s, key)))
+        if (!(qj = job_new(s, key, 1)))
+            return; /* OOM */
     
     gettimeofday(&qj->delivery, NULL);
     job_set_elapse_time(s, qj, AVAHI_QUERY_HISTORY_MSEC, 0);
 }
 
+int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id) {
+    AvahiQueryJob *qj;
+    
+    assert(s);
+
+    /* Very short lived queries can withdraw an already scheduled item
+     * from the queue using this function, simply by passing the id
+     * returned by avahi_query_scheduler_post(). */
+
+    for (qj = s->jobs; qj; qj = qj->jobs_next) {
+        assert(!qj->done);
+        
+        if (qj->id == id) {
+            /* Entry found */
+
+            assert(qj->n_posted >= 1);
+
+            if (--qj->n_posted <= 0) {
+
+                /* We withdraw this job only if the calling object was
+                 * the only remaining poster. (Usually this is the
+                 * case since there should exist only one querier per
+                 * key, but there are exceptions, notably reflected
+                 * traffic.) */
+                
+                job_free(s, qj);
+                return 1;
+            }
+        }
+    }
+
+    return 0;
+}
index 7b281b6389b099090d174911634a19474fb6aec3..52385583afe1a86cb0083f4a8c4eca180c12e688 100644 (file)
@@ -31,7 +31,8 @@ AvahiQueryScheduler *avahi_query_scheduler_new(AvahiInterface *i);
 void avahi_query_scheduler_free(AvahiQueryScheduler *s);
 void avahi_query_scheduler_clear(AvahiQueryScheduler *s);
 
-int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately);
+int avahi_query_scheduler_post(AvahiQueryScheduler *s, AvahiKey *key, int immediately, unsigned *ret_id);
+int avahi_query_scheduler_withdraw_by_id(AvahiQueryScheduler *s, unsigned id);
 void avahi_query_scheduler_incoming(AvahiQueryScheduler *s, AvahiKey *key);
 
 #endif
index 9d766844c022e4286c6f659eb7c9883bcfdf62f4..915ecbb87bccb3297e5d5187c6d4d4978ebfdf33 100644 (file)
@@ -43,10 +43,11 @@ struct AvahiRecordListItem {
     AVAHI_LLIST_FIELDS(AvahiRecordListItem, items);
 };
 
-
 struct AvahiRecordList {
     AVAHI_LLIST_HEAD(AvahiRecordListItem, read);
     AVAHI_LLIST_HEAD(AvahiRecordListItem, unread);
+
+    int all_flush_cache;
 };
 
 AvahiRecordList *avahi_record_list_new(void) {
@@ -59,6 +60,8 @@ AvahiRecordList *avahi_record_list_new(void) {
     
     AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->read);
     AVAHI_LLIST_HEAD_INIT(AvahiRecordListItem, l->unread);
+
+    l->all_flush_cache = 1;
     return l;
 }
 
@@ -89,9 +92,11 @@ void avahi_record_list_flush(AvahiRecordList *l) {
         item_free(l, l->read);
     while (l->unread)
         item_free(l, l->unread);
+
+    l->all_flush_cache = 1;
 }
 
-AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *flush_cache, int *unicast_response, int *auxiliary) {
+AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary) {
     AvahiRecord *r;
     AvahiRecordListItem *i;
 
@@ -101,12 +106,12 @@ AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *flush_cache, int *u
     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;
+    if (ret_unicast_response)
+        *ret_unicast_response = i->unicast_response;
+    if (ret_flush_cache)
+        *ret_flush_cache = i->flush_cache;
+    if (ret_auxiliary)
+        *ret_auxiliary = i->auxiliary;
 
     AVAHI_LLIST_REMOVE(AvahiRecordListItem, items, l->unread, i);
     AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->read, i);
@@ -153,6 +158,8 @@ void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache,
     i->record = avahi_record_ref(r);
     i->read = 0;
 
+    l->all_flush_cache = l->all_flush_cache && flush_cache;
+    
     AVAHI_LLIST_PREPEND(AvahiRecordListItem, items, l->unread, i);
 }
 
@@ -173,3 +180,11 @@ int avahi_record_list_is_empty(AvahiRecordList *l) {
     
     return !l->unread && !l->read;
 }
+
+int avahi_record_list_all_flush_cache(AvahiRecordList *l) {
+    assert(l);
+
+    /* Return TRUE if all entries in this list have flush_cache set */
+    
+    return l->all_flush_cache;
+}
index 200dae8442e751ca0a4ec48054ff2ff98e341fb1..9c07ecd5acca8bd0bc89810297b16fea0775506b 100644 (file)
@@ -31,10 +31,12 @@ AvahiRecordList *avahi_record_list_new(void);
 void avahi_record_list_free(AvahiRecordList *l);
 void avahi_record_list_flush(AvahiRecordList *l);
 
-AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *flush_cache, int *unicast_response, int *auxiliary);
+AvahiRecord* avahi_record_list_next(AvahiRecordList *l, int *ret_flush_cache, int *ret_unicast_response, int *ret_auxiliary);
 void avahi_record_list_push(AvahiRecordList *l, AvahiRecord *r, int flush_cache, int unicast_response, int auxiliary);
 void avahi_record_list_drop(AvahiRecordList *l, AvahiRecord *r);
 
+int avahi_record_list_all_flush_cache(AvahiRecordList *l);
+
 int avahi_record_list_is_empty(AvahiRecordList *l);
 
 #endif
index eb04ec1cef394d2681fd597be32a652ac696cf40..5790beed77195ba53bcd8f97d38cda478f24919b 100644 (file)
@@ -404,15 +404,25 @@ void avahi_server_generate_response(AvahiServer *s, AvahiInterface *i, AvahiDnsP
         int tc = p && !!(avahi_dns_packet_get_field(p, AVAHI_DNS_FIELD_FLAGS) & AVAHI_DNS_FLAG_TC);
         
         while ((r = avahi_record_list_next(s->record_list, &flush_cache, &unicast_response, &auxiliary))) {
-                        
-            if (!avahi_interface_post_response(i, r, flush_cache, a, immediately || (flush_cache && !tc && !auxiliary)) && unicast_response) {
 
-                append_aux_records_to_list(s, i, r, unicast_response);
-                
+            int im = immediately;
+
+            /* Only send the response immediately if it contains a
+             * unique entry AND it is not in reply to a truncated
+             * packet AND it is not an auxiliary record AND all other
+             * responses for this record are unique too. */
+            
+            if (flush_cache && !tc && !auxiliary && avahi_record_list_all_flush_cache(s->record_list))
+                im = 1;
+
+            if (!avahi_interface_post_response(i, r, flush_cache, a, im) && 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 */
 
+                append_aux_records_to_list(s, i, r, unicast_response);
+                
                 for (;;) {
                 
                     if (!reply) {
@@ -517,7 +527,7 @@ static void reflect_query(AvahiServer *s, AvahiInterface *i, AvahiKey *k) {
     for (j = s->monitor->interfaces; j; j = j->interface_next)
         if (j != i && (s->config.reflect_ipv || j->protocol == i->protocol)) {
             /* Post the query to other networks */
-            avahi_interface_post_query(j, k, 1);
+            avahi_interface_post_query(j, k, 1, NULL);
 
             /* Reply from caches of other network. This is needed to
              * "work around" known answer suppression. */