diff options
Diffstat (limited to 'avahi-core')
-rw-r--r-- | avahi-core/cache.c | 8 | ||||
-rw-r--r-- | avahi-core/iface.c | 9 | ||||
-rw-r--r-- | avahi-core/iface.h | 3 | ||||
-rw-r--r-- | avahi-core/multicast-lookup.c | 3 | ||||
-rw-r--r-- | avahi-core/querier.c | 96 | ||||
-rw-r--r-- | avahi-core/querier.h | 2 | ||||
-rw-r--r-- | avahi-core/query-sched.c | 80 | ||||
-rw-r--r-- | avahi-core/query-sched.h | 3 | ||||
-rw-r--r-- | avahi-core/rrlist.c | 31 | ||||
-rw-r--r-- | avahi-core/rrlist.h | 4 | ||||
-rw-r--r-- | avahi-core/server.c | 20 |
11 files changed, 205 insertions, 54 deletions
diff --git a/avahi-core/cache.c b/avahi-core/cache.c index 5e87706..c26b6bb 100644 --- a/avahi-core/cache.c +++ b/avahi-core/cache.c @@ -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); diff --git a/avahi-core/iface.c b/avahi-core/iface.c index 0e3ef68..b466f48 100644 --- a/avahi-core/iface.c +++ b/avahi-core/iface.c @@ -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); diff --git a/avahi-core/iface.h b/avahi-core/iface.h index 30d3d22..4106ea7 100644 --- a/avahi-core/iface.h +++ b/avahi-core/iface.h @@ -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); diff --git a/avahi-core/multicast-lookup.c b/avahi-core/multicast-lookup.c index fa8c859..8101df5 100644 --- a/avahi-core/multicast-lookup.c +++ b/avahi-core/multicast-lookup.c @@ -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; diff --git a/avahi-core/querier.c b/avahi-core/querier.c index 41acc24..b1069d1 100644 --- a/avahi-core/querier.c +++ b/avahi-core/querier.c @@ -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) { diff --git a/avahi-core/querier.h b/avahi-core/querier.h index 3ec4564..3f4eead 100644 --- a/avahi-core/querier.h +++ b/avahi-core/querier.h @@ -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 diff --git a/avahi-core/query-sched.c b/avahi-core/query-sched.c index 0096838..f875411 100644 --- a/avahi-core/query-sched.c +++ b/avahi-core/query-sched.c @@ -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; +} diff --git a/avahi-core/query-sched.h b/avahi-core/query-sched.h index 7b281b6..5238558 100644 --- a/avahi-core/query-sched.h +++ b/avahi-core/query-sched.h @@ -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 diff --git a/avahi-core/rrlist.c b/avahi-core/rrlist.c index 9d76684..915ecbb 100644 --- a/avahi-core/rrlist.c +++ b/avahi-core/rrlist.c @@ -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; +} diff --git a/avahi-core/rrlist.h b/avahi-core/rrlist.h index 200dae8..9c07ecd 100644 --- a/avahi-core/rrlist.h +++ b/avahi-core/rrlist.h @@ -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 diff --git a/avahi-core/server.c b/avahi-core/server.c index eb04ec1..5790bee 100644 --- a/avahi-core/server.c +++ b/avahi-core/server.c @@ -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. */ |