static AvahiResponseJob* find_suppressed_job(AvahiResponseScheduler *s, AvahiRecord *record, const AvahiAddress *querier) { AvahiResponseJob *rj; assert(s); assert(record); assert(querier); for (rj = s->suppressed; rj; rj = rj->jobs_next) { assert(rj->state == AVAHI_SUPPRESSED); 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; }
static AvahiResponseJob* find_history_job(AvahiResponseScheduler *s, AvahiRecord *record) { AvahiResponseJob *rj; assert(s); assert(record); for (rj = s->history; rj; rj = rj->jobs_next) { assert(rj->state == AVAHI_DONE); if (avahi_record_equal_no_ttl(rj->record, record)) { /* Check whether this entry is outdated */ /* avahi_log_debug("history age: %u", (unsigned) (avahi_age(&rj->delivery)/1000)); */ if (avahi_age(&rj->delivery)/1000 > AVAHI_RESPONSE_HISTORY_MSEC) { /* it is outdated, so let's remove it */ job_free(s, rj); return NULL; } return rj; } } return NULL; }
static void* lookup_record_callback(AvahiCache *c, AvahiKey *pattern, AvahiCacheEntry *e, void *userdata) { assert(c); assert(pattern); assert(e); if (avahi_record_equal_no_ttl(e->record, userdata)) return e; return NULL; }
static AvahiWideAreaCacheEntry* find_record_in_cache(AvahiWideAreaLookupEngine *e, AvahiRecord *r) { AvahiWideAreaCacheEntry *c; assert(e); assert(r); for (c = avahi_hashmap_lookup(e->cache_by_key, r->key); c; c = c->by_key_next) if (avahi_record_equal_no_ttl(r, c->record)) return c; return NULL; }
static AvahiProbeJob* find_scheduled_job(AvahiProbeScheduler *s, AvahiRecord *record) { AvahiProbeJob *pj; assert(s); assert(record); for (pj = s->jobs; pj; pj = pj->jobs_next) { assert(!pj->done); if (avahi_record_equal_no_ttl(pj->record, record)) return pj; } return NULL; }
static AvahiResponseJob* find_scheduled_job(AvahiResponseScheduler *s, AvahiRecord *record) { AvahiResponseJob *rj; assert(s); assert(record); for (rj = s->jobs; rj; rj = rj->jobs_next) { assert(rj->state == AVAHI_SCHEDULED); if (avahi_record_equal_no_ttl(rj->record, record)) return rj; } return NULL; }
static int is_duplicate_entry(AvahiServer *s, AvahiEntry *e) { AvahiEntry *i; assert(s); assert(e); for (i = avahi_hashmap_lookup(s->entries_by_key, e->record->key); i; i = i->by_key_next) { if ((i == e) || (i->dead)) continue; if (!avahi_record_equal_no_ttl(i->record, e->record)) continue; return 1; } return 0; }
static int is_duplicate_entry(AvahiServer *s, AvahiEntry *e) { AvahiEntry *i; assert(s); assert(e && e->type == AVAHI_ENTRY_MDNS); for (i = avahi_hashmap_lookup(s->mdns.entries_by_key, e->record->key); i; i = i->by_key_next) { assert(i->type == AVAHI_ENTRY_MDNS); if (i == e) continue; if (!avahi_record_equal_no_ttl(i->record, e->record)) continue; return 1; } return 0; }
static AvahiProbeJob* find_history_job(AvahiProbeScheduler *s, AvahiRecord *record) { AvahiProbeJob *pj; assert(s); assert(record); for (pj = s->history; pj; pj = pj->jobs_next) { assert(pj->done); if (avahi_record_equal_no_ttl(pj->record, record)) { /* Check whether this entry is outdated */ if (avahi_age(&pj->delivery) > AVAHI_PROBE_HISTORY_MSEC*1000) { /* it is outdated, so let's remove it */ job_free(s, pj); return NULL; } return pj; } } return NULL; }
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; } }
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; }
int main(AVAHI_GCC_UNUSED int argc, AVAHI_GCC_UNUSED char *argv[]) { char t[AVAHI_DOMAIN_NAME_MAX], *m; const char *a, *b, *c, *d; AvahiDnsPacket *p; AvahiRecord *r, *r2; uint8_t rdata[AVAHI_DNS_RDATA_MAX]; size_t l; p = avahi_dns_packet_new(0); assert(avahi_dns_packet_append_name(p, a = "Ahello.hello.hello.de.")); assert(avahi_dns_packet_append_name(p, b = "Bthis is a test.hello.de.")); assert(avahi_dns_packet_append_name(p, c = "Cthis\\.is\\.a\\.test\\.with\\.dots.hello.de.")); assert(avahi_dns_packet_append_name(p, d = "Dthis\\\\is another test.hello.de.")); avahi_hexdump(AVAHI_DNS_PACKET_DATA(p), p->size); assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); avahi_log_debug(">%s<", t); assert(avahi_domain_equal(a, t)); assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); avahi_log_debug(">%s<", t); assert(avahi_domain_equal(b, t)); assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); avahi_log_debug(">%s<", t); assert(avahi_domain_equal(c, t)); assert(avahi_dns_packet_consume_name(p, t, sizeof(t)) == 0); avahi_log_debug(">%s<", t); assert(avahi_domain_equal(d, t)); avahi_dns_packet_free(p); /* RDATA PARSING AND SERIALIZATION */ /* Create an AvahiRecord with some usful data */ r = avahi_record_new_full("foobar.local", AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_HINFO, AVAHI_DEFAULT_TTL); assert(r); r->data.hinfo.cpu = avahi_strdup("FOO"); r->data.hinfo.os = avahi_strdup("BAR"); /* Serialize it into a blob */ assert((l = avahi_rdata_serialize(r, rdata, sizeof(rdata))) != (size_t) -1); /* Print it */ avahi_hexdump(rdata, l); /* Create a new record and fill in the data from the blob */ r2 = avahi_record_new(r->key, AVAHI_DEFAULT_TTL); assert(r2); assert(avahi_rdata_parse(r2, rdata, l) >= 0); /* Compare both versions */ assert(avahi_record_equal_no_ttl(r, r2)); /* Free the records */ avahi_record_unref(r); avahi_record_unref(r2); r = avahi_record_new_full("foobar", 77, 77, AVAHI_DEFAULT_TTL); assert(r); assert(r->data.generic.data = avahi_memdup("HALLO", r->data.generic.size = 5)); m = avahi_record_to_string(r); assert(m); avahi_log_debug(">%s<", m); avahi_free(m); avahi_record_unref(r); return 0; }
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); */ }