static AvahiResponseJob* job_new(AvahiResponseScheduler *s, AvahiRecord *record, AvahiResponseJobState state) { AvahiResponseJob *rj; assert(s); assert(record); if (!(rj = avahi_new(AvahiResponseJob, 1))) { avahi_log_error(__FILE__": Out of memory"); return NULL; } rj->scheduler = s; rj->record = avahi_record_ref(record); rj->time_event = NULL; rj->flush_cache = 0; rj->querier_valid = 0; 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 add_to_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { AvahiWideAreaCacheEntry *c; int is_new; assert(e); assert(r); if ((c = find_record_in_cache(e, r))) { is_new = 0; /* Update the existing entry */ avahi_record_unref(c->record); } else { AvahiWideAreaCacheEntry *t; is_new = 1; /* Enforce cache size */ if (e->cache_n_entries >= CACHE_ENTRIES_MAX) /* Eventually we should improve the caching algorithm here */ goto finish; c = avahi_new(AvahiWideAreaCacheEntry, 1); c->engine = e; c->time_event = NULL; AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, cache, e->cache, c); /* Add the new entry to the cache entry hash table */ t = avahi_hashmap_lookup(e->cache_by_key, r->key); AVAHI_LLIST_PREPEND(AvahiWideAreaCacheEntry, by_key, t, c); avahi_hashmap_replace(e->cache_by_key, avahi_key_ref(r->key), t); e->cache_n_entries ++; } c->record = avahi_record_ref(r); gettimeofday(&c->timestamp, NULL); c->expiry = c->timestamp; avahi_timeval_add(&c->expiry, r->ttl * 1000000); if (c->time_event) avahi_time_event_update(c->time_event, &c->expiry); else c->time_event = avahi_time_event_new(e->server->time_event_queue, &c->expiry, expiry_event, c); finish: if (is_new) run_callbacks(e, r); }
const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) { AvahiEntry **e = (AvahiEntry**) state; assert(s); assert(e); if (!*e) *e = g ? g->entries : s->entries; while (*e && (*e)->dead) *e = g ? (*e)->by_group_next : (*e)->entries_next; if (!*e) return NULL; return avahi_record_ref((*e)->record); }
static void* known_answer_walk_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void* userdata) { AvahiQueryScheduler *s = userdata; AvahiKnownAnswer *ka; assert(c); assert(pattern); assert(e); assert(s); if (avahi_cache_entry_half_ttl(c, e)) return NULL; if (!(ka = avahi_new0(AvahiKnownAnswer, 1))) { avahi_log_error(__FILE__": Out of memory"); return NULL; } ka->scheduler = s; ka->record = avahi_record_ref(e->record); AVAHI_LLIST_PREPEND(AvahiKnownAnswer, known_answer, s->known_answers, ka); return NULL; }
static AvahiProbeJob* job_new(AvahiProbeScheduler *s, AvahiRecord *record, int done) { AvahiProbeJob *pj; assert(s); assert(record); if (!(pj = avahi_new(AvahiProbeJob, 1))) { avahi_log_error(__FILE__": Out of memory"); return NULL; /* OOM */ } pj->scheduler = s; pj->record = avahi_record_ref(record); pj->time_event = NULL; pj->chosen = 0; if ((pj->done = done)) AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->history, pj); else AVAHI_LLIST_PREPEND(AvahiProbeJob, jobs, s->jobs, pj); return pj; }
void avahi_response_scheduler_suppress(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) { AvahiResponseJob *rj; assert(s); assert(record); 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 */ /* avahi_log_debug("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 */ if (!(rj = job_new(s, record, AVAHI_SUPPRESSED))) return; /* OOM */ rj->querier_valid = 1; rj->querier = *querier; } gettimeofday(&rj->delivery, NULL); job_set_elapse_time(s, rj, AVAHI_RESPONSE_SUPPRESS_MSEC, 0); }
void avahi_response_scheduler_incoming(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache) { AvahiResponseJob *rj; 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 */ /* avahi_log_debug("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 */ if (!(rj = job_new(s, record, AVAHI_DONE))) return; /* OOM */ rj->flush_cache = flush_cache; rj->querier_valid = 0; gettimeofday(&rj->delivery, NULL); job_set_elapse_time(s, rj, AVAHI_RESPONSE_HISTORY_MSEC, 0); }
static void record_browser_callback( AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, AvahiLookupResultFlags flags, void* userdata) { AvahiSServiceResolver *r = userdata; assert(rr); assert(r); if (rr == r->record_browser_aaaa || rr == r->record_browser_a) r->address_flags = flags; else if (rr == r->record_browser_srv) r->srv_flags = flags; else if (rr == r->record_browser_txt) r->txt_flags = flags; switch (event) { case AVAHI_BROWSER_NEW: { int changed = 0; assert(record); if (r->interface > 0 && interface > 0 && interface != r->interface) return; if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) return; if (r->interface <= 0) r->interface = interface; if (r->protocol == AVAHI_PROTO_UNSPEC) r->protocol = protocol; switch (record->key->type) { case AVAHI_DNS_TYPE_SRV: if (!r->srv_record) { r->srv_record = avahi_record_ref(record); changed = 1; if (r->record_browser_a) { avahi_s_record_browser_free(r->record_browser_a); r->record_browser_a = NULL; } if (r->record_browser_aaaa) { avahi_s_record_browser_free(r->record_browser_aaaa); r->record_browser_aaaa = NULL; } if (!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)) { if (r->address_protocol == AVAHI_PROTO_INET || r->address_protocol == AVAHI_PROTO_UNSPEC) { AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A); r->record_browser_a = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); avahi_key_unref(k); } if (r->address_protocol == AVAHI_PROTO_INET6 || r->address_protocol == AVAHI_PROTO_UNSPEC) { AvahiKey *k = avahi_key_new(r->srv_record->data.srv.name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA); r->record_browser_aaaa = avahi_s_record_browser_new(r->server, r->interface, r->protocol, k, r->user_flags & ~(AVAHI_LOOKUP_NO_TXT|AVAHI_LOOKUP_NO_ADDRESS), record_browser_callback, r); avahi_key_unref(k); } } } break; case AVAHI_DNS_TYPE_TXT: assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); if (!r->txt_record) { r->txt_record = avahi_record_ref(record); changed = 1; } break; case AVAHI_DNS_TYPE_A: case AVAHI_DNS_TYPE_AAAA: assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); if (!r->address_record) { r->address_record = avahi_record_ref(record); changed = 1; } break; default: abort(); } if (changed && r->srv_record && (r->txt_record || (r->user_flags & AVAHI_LOOKUP_NO_TXT)) && (r->address_record || (r->user_flags & AVAHI_LOOKUP_NO_ADDRESS))) finish(r, AVAHI_RESOLVER_FOUND); break; } case AVAHI_BROWSER_REMOVE: assert(record); switch (record->key->type) { case AVAHI_DNS_TYPE_SRV: if (r->srv_record && avahi_record_equal_no_ttl(record, r->srv_record)) { avahi_record_unref(r->srv_record); r->srv_record = NULL; if (r->record_browser_a) { avahi_s_record_browser_free(r->record_browser_a); r->record_browser_a = NULL; } if (r->record_browser_aaaa) { avahi_s_record_browser_free(r->record_browser_aaaa); r->record_browser_aaaa = NULL; } /** Look for a replacement */ avahi_s_record_browser_restart(r->record_browser_srv); start_timeout(r); } break; case AVAHI_DNS_TYPE_TXT: assert(!(r->user_flags & AVAHI_LOOKUP_NO_TXT)); if (r->txt_record && avahi_record_equal_no_ttl(record, r->txt_record)) { avahi_record_unref(r->txt_record); r->txt_record = NULL; /** Look for a replacement */ avahi_s_record_browser_restart(r->record_browser_txt); start_timeout(r); } break; case AVAHI_DNS_TYPE_A: case AVAHI_DNS_TYPE_AAAA: assert(!(r->user_flags & AVAHI_LOOKUP_NO_ADDRESS)); if (r->address_record && avahi_record_equal_no_ttl(record, r->address_record)) { avahi_record_unref(r->address_record); r->address_record = NULL; /** Look for a replacement */ if (r->record_browser_aaaa) avahi_s_record_browser_restart(r->record_browser_aaaa); if (r->record_browser_a) avahi_s_record_browser_restart(r->record_browser_a); start_timeout(r); } break; default: abort(); } break; case AVAHI_BROWSER_CACHE_EXHAUSTED: case AVAHI_BROWSER_ALL_FOR_NOW: break; case AVAHI_BROWSER_FAILURE: if (rr == r->record_browser_a && r->record_browser_aaaa) { /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */ avahi_s_record_browser_free(r->record_browser_a); r->record_browser_a = NULL; break; } if (rr == r->record_browser_aaaa && r->record_browser_a) { /* We were looking for both AAAA and A, and the other query is still living, so we'll not die */ avahi_s_record_browser_free(r->record_browser_aaaa); r->record_browser_aaaa = NULL; break; } /* Hmm, everything's lost, tell the user */ if (r->record_browser_srv) avahi_s_record_browser_free(r->record_browser_srv); if (r->record_browser_txt) avahi_s_record_browser_free(r->record_browser_txt); if (r->record_browser_a) avahi_s_record_browser_free(r->record_browser_a); if (r->record_browser_aaaa) avahi_s_record_browser_free(r->record_browser_aaaa); r->record_browser_srv = r->record_browser_txt = r->record_browser_a = r->record_browser_aaaa = NULL; finish(r, AVAHI_RESOLVER_FAILURE); break; } }
int avahi_response_scheduler_post(AvahiResponseScheduler *s, AvahiRecord *record, int flush_cache, const AvahiAddress *querier, int immediately) { AvahiResponseJob *rj; struct timeval tv; /* char *t; */ assert(s); assert(record); assert(!avahi_key_is_pattern(record->key)); /* t = avahi_record_to_string(record); */ /* avahi_log_debug("post %i %s", immediately, t); */ /* avahi_free(t); */ /* 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) { /* avahi_log_debug("Response suppressed by known answer suppression."); */ return 0; } /* 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)) { /* avahi_log_debug("Response suppressed by local duplicate suppression (history)"); */ return 0; } /* 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))) { /* avahi_log_debug("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_update(rj->time_event, &rj->delivery); } /* Update the flush cache bit */ if (flush_cache) rj->flush_cache = 1; /* Update the querier field */ if (!querier || (rj->querier_valid && avahi_address_cmp(querier, &rj->querier) != 0)) rj->querier_valid = 0; /* Update record data (just for the TTL) */ avahi_record_unref(rj->record); rj->record = avahi_record_ref(record); return 1; } else { /* avahi_log_debug("Accepted new response job."); */ /* Create a new job and schedule it */ if (!(rj = job_new(s, record, AVAHI_SCHEDULED))) return 0; /* OOM */ rj->delivery = tv; rj->time_event = avahi_time_event_new(s->time_event_queue, &rj->delivery, elapse_callback, rj); rj->flush_cache = flush_cache; if ((rj->querier_valid = !!querier)) rj->querier = *querier; return 1; } }
static void record_browser_callback( AvahiSRecordBrowser*rr, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, AvahiRecord *record, AvahiLookupResultFlags flags, void* userdata) { AvahiSAddressResolver *r = userdata; assert(rr); assert(r); switch (event) { case AVAHI_BROWSER_NEW: assert(record); assert(record->key->type == AVAHI_DNS_TYPE_PTR); if (flags & AVAHI_LOOKUP_RESULT_LLMNR) r->llmnr_has_record = 1; if (r->interface > 0 && interface != r->interface) return; if (r->protocol != AVAHI_PROTO_UNSPEC && protocol != r->protocol) return; if (r->interface <= 0) r->interface = interface; if (r->protocol == AVAHI_PROTO_UNSPEC) r->protocol = protocol; if (!r->ptr_record) { r->ptr_record = avahi_record_ref(record); r->flags = flags; finish(r, AVAHI_RESOLVER_FOUND); } break; case AVAHI_BROWSER_REMOVE: assert(record); assert(record->key->type == AVAHI_DNS_TYPE_PTR); if (r->ptr_record && avahi_record_equal_no_ttl(record, r->ptr_record)) { avahi_record_unref(r->ptr_record); r->ptr_record = NULL; r->flags = flags; /** Look for a replacement */ avahi_s_record_browser_restart(r->record_browser); start_timeout(r); } break; case AVAHI_BROWSER_CACHE_EXHAUSTED: break; case AVAHI_BROWSER_ALL_FOR_NOW: /* when called by _LLMNR we trigger lookup by _MULTICAST*/ if ((flags & AVAHI_LOOKUP_RESULT_LLMNR) && (r->retry_with_multicast) && !r->llmnr_has_record) { r->retry_with_multicast = 0; avahi_s_record_browser_free(r->record_browser); r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_MULTICAST, record_browser_callback, r); if (r->record_browser) { start_timeout(r); break; } } break; case AVAHI_BROWSER_FAILURE: if (r->retry_with_llmnr) { r->retry_with_llmnr = 0; avahi_s_record_browser_free(r->record_browser); r->record_browser = avahi_s_record_browser_new(r->server, r->interface, r->protocol, r->key, AVAHI_LOOKUP_USE_LLMNR, record_browser_callback, r); if (r->record_browser) { start_timeout(r); break; } } break; } }
static AvahiEntry * server_add_internal( AvahiServer *s, AvahiSEntryGroup *g, AvahiIfIndex interface, AvahiProtocol protocol, AvahiPublishFlags flags, AvahiRecord *r) { AvahiEntry *e; assert(s); assert(r); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, s->state != AVAHI_SERVER_FAILURE && s->state != AVAHI_SERVER_INVALID, AVAHI_ERR_BAD_STATE); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID( flags, AVAHI_PUBLISH_NO_ANNOUNCE| AVAHI_PUBLISH_NO_PROBE| AVAHI_PUBLISH_UNIQUE| AVAHI_PUBLISH_ALLOW_MULTIPLE| AVAHI_PUBLISH_UPDATE| AVAHI_PUBLISH_USE_WIDE_AREA| AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(r->key->name), AVAHI_ERR_INVALID_HOST_NAME); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, AVAHI_ERR_INVALID_TTL); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !avahi_key_is_pattern(r->key), AVAHI_ERR_IS_PATTERN); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_record_is_valid(r), AVAHI_ERR_INVALID_RECORD); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == AVAHI_DNS_CLASS_IN, AVAHI_ERR_INVALID_DNS_CLASS); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, (r->key->type != 0) && (r->key->type != AVAHI_DNS_TYPE_ANY) && (r->key->type != AVAHI_DNS_TYPE_OPT) && (r->key->type != AVAHI_DNS_TYPE_TKEY) && (r->key->type != AVAHI_DNS_TYPE_TSIG) && (r->key->type != AVAHI_DNS_TYPE_IXFR) && (r->key->type != AVAHI_DNS_TYPE_AXFR), AVAHI_ERR_INVALID_DNS_TYPE); transport_flags_from_domain(s, &flags, r->key->name); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, AVAHI_ERR_NOT_PERMITTED); AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !g || (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) || (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE); if (flags & AVAHI_PUBLISH_UPDATE) { AvahiRecord *old_record; int is_first = 1; /* Update and existing record */ /* Find the first matching entry */ for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) { if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol) break; is_first = 0; } /* Hmm, nothing found? */ if (!e) { avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND); return NULL; } /* Update the entry */ old_record = e->record; e->record = avahi_record_ref(r); e->flags = flags; /* Announce our changes when needed */ if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) { /* Remove the old entry from all caches, if needed */ if (!(e->flags & AVAHI_PUBLISH_UNIQUE)) avahi_goodbye_entry(s, e, 1, 0); /* Reannounce our updated entry */ avahi_reannounce_entry(s, e); } /* If we were the first entry in the list, we need to update the key */ if (is_first) avahi_hashmap_replace(s->entries_by_key, e->record->key, e); avahi_record_unref(old_record); } else { AvahiEntry *t; /* Add a new record */ if (check_record_conflict(s, interface, protocol, r, flags) < 0) { avahi_server_set_errno(s, AVAHI_ERR_COLLISION); return NULL; } if (!(e = avahi_new(AvahiEntry, 1))) { avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY); return NULL; } e->server = s; e->record = avahi_record_ref(r); e->group = g; e->interface = interface; e->protocol = protocol; e->flags = flags; e->dead = 0; AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers); AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e); /* Insert into hash table indexed by name */ t = avahi_hashmap_lookup(s->entries_by_key, e->record->key); AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e); avahi_hashmap_replace(s->entries_by_key, e->record->key, t); /* Insert into group list */ if (g) AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); avahi_announce_entry(s, e); } return e; }
void avahi_cache_update(AvahiCache *c, AvahiRecord *r, int cache_flush, const AvahiAddress *a) { /* char *txt; */ assert(c); assert(r && r->ref >= 1); /* txt = avahi_record_to_string(r); */ if (r->ttl == 0) { /* This is a goodbye request */ AvahiCacheEntry *e; if ((e = lookup_record(c, r))) expire_in_one_second(c, e, AVAHI_CACHE_GOODBYE_FINAL); } else { AvahiCacheEntry *e = NULL, *first; struct timeval now; gettimeofday(&now, NULL); /* This is an update request */ if ((first = lookup_key(c, r->key))) { if (cache_flush) { /* For unique entries drop all entries older than one second */ for (e = first; e; e = e->by_key_next) { AvahiUsec t; t = avahi_timeval_diff(&now, &e->timestamp); if (t > 1000000) expire_in_one_second(c, e, AVAHI_CACHE_REPLACE_FINAL); } } /* Look for exactly the same entry */ for (e = first; e; e = e->by_key_next) if (avahi_record_equal_no_ttl(e->record, r)) break; } if (e) { /* avahi_log_debug("found matching cache entry"); */ /* We need to update the hash table key if we replace the * record */ if (e->by_key_prev == NULL) avahi_hashmap_replace(c->hashmap, r->key, e); /* Update the record */ avahi_record_unref(e->record); e->record = avahi_record_ref(r); /* avahi_log_debug("cache: updating %s", txt); */ } else { /* No entry found, therefore we create a new one */ /* avahi_log_debug("cache: couldn't find matching cache entry for %s", txt); */ if (c->n_entries >= AVAHI_CACHE_ENTRIES_MAX) return; if (!(e = avahi_new(AvahiCacheEntry, 1))) { avahi_log_error(__FILE__": Out of memory"); return; } e->cache = c; e->time_event = NULL; e->record = avahi_record_ref(r); /* Append to hash table */ AVAHI_LLIST_PREPEND(AvahiCacheEntry, by_key, first, e); avahi_hashmap_replace(c->hashmap, e->record->key, first); /* Append to linked list */ AVAHI_LLIST_PREPEND(AvahiCacheEntry, entry, c->entries, e); c->n_entries++; /* Notify subscribers */ avahi_multicast_lookup_engine_notify(c->server->mdns.multicast_lookup_engine, c->interface, e->record, AVAHI_BROWSER_NEW); } e->origin = *a; e->timestamp = now; next_expiry(c, e, 80); e->state = AVAHI_CACHE_VALID; e->cache_flush = cache_flush; } /* avahi_free(txt); */ }