From d553a1c2d1cd3fcdd65ade64940b5bd3efc70675 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 31 Mar 2005 23:31:11 +0000 Subject: [PATCH] add client part of known answer suppresion git-svn-id: file:///home/lennart/svn/public/avahi/trunk@20 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- cache.c | 153 +++++++++++++++++++++++++++++++++++++--------------- cache.h | 11 +++- dns.c | 29 ++++++++-- dns.h | 5 +- main.c | 14 ++--- psched.c | 109 ++++++++++++++++++++++++++++--------- psched.h | 9 ++++ rr.c | 24 ++++++++- rr.h | 9 +++- server.c | 37 +++++++++---- subscribe.c | 44 ++++++++++++--- subscribe.h | 2 + todo | 13 +++-- 13 files changed, 351 insertions(+), 108 deletions(-) diff --git a/cache.c b/cache.c index 99d79fb..bbf667b 100644 --- a/cache.c +++ b/cache.c @@ -1,22 +1,26 @@ #include +#include "util.h" #include "cache.h" -static void remove_entry(flxCache *c, flxCacheEntry *e, gboolean remove_from_hash_table) { +static void remove_entry(flxCache *c, flxCacheEntry *e) { + flxCacheEntry *t; + g_assert(c); g_assert(e); - g_message("removing from cache: %p %p", c, e); - - if (remove_from_hash_table) { - flxCacheEntry *t; - t = g_hash_table_lookup(c->hash_table, e->record->key); - FLX_LLIST_REMOVE(flxCacheEntry, by_name, t, e); - if (t) - g_hash_table_replace(c->hash_table, t->record->key, t); - else - g_hash_table_remove(c->hash_table, e->record->key); - } +/* g_message("removing from cache: %p %p", c, e); */ + + /* Remove from hash table */ + t = g_hash_table_lookup(c->hash_table, e->record->key); + FLX_LLIST_REMOVE(flxCacheEntry, by_key, t, e); + if (t) + g_hash_table_replace(c->hash_table, t->record->key, t); + else + g_hash_table_remove(c->hash_table, e->record->key); + + /* Remove from linked list */ + FLX_LLIST_REMOVE(flxCacheEntry, entry, c->entries, e); if (e->time_event) flx_time_event_queue_remove(c->server->time_event_queue, e->time_event); @@ -37,24 +41,17 @@ flxCache *flx_cache_new(flxServer *server, flxInterface *iface) { c->interface = iface; c->hash_table = g_hash_table_new((GHashFunc) flx_key_hash, (GEqualFunc) flx_key_equal); - return c; -} - -gboolean remove_func(gpointer key, gpointer value, gpointer user_data) { - flxCacheEntry *e, *next; - - for (e = value; e; e = next) { - next = e->by_name_next; - remove_entry(user_data, e, FALSE); - } + FLX_LLIST_HEAD_INIT(flxCacheEntry, c->entries); - return TRUE; + return c; } void flx_cache_free(flxCache *c) { g_assert(c); - g_hash_table_foreach_remove(c->hash_table, remove_func, c); + while (c->entries) + remove_entry(c, c->entries); + g_hash_table_destroy(c->hash_table); g_free(c); @@ -64,19 +61,61 @@ flxCacheEntry *flx_cache_lookup_key(flxCache *c, flxKey *k) { g_assert(c); g_assert(k); + g_assert(!flx_key_is_pattern(k)); + return g_hash_table_lookup(c->hash_table, k); } +gpointer flx_cache_walk(flxCache *c, flxKey *pattern, flxCacheWalkCallback cb, gpointer userdata) { + gpointer ret; + + g_assert(c); + g_assert(pattern); + g_assert(cb); + + if (flx_key_is_pattern(pattern)) { + flxCacheEntry *e, *n; + + for (e = c->entries; e; e = n) { + n = e->entry_next; + + if (flx_key_pattern_match(pattern, e->record->key)) + if ((ret = cb(c, pattern, e, userdata))) + return ret; + } + + } else { + flxCacheEntry *e, *n; + + for (e = flx_cache_lookup_key(c, pattern); e; e = n) { + n = e->by_key_next; + + if ((ret = cb(c, pattern, e, userdata))) + return ret; + } + } + + return NULL; +} + +static gpointer lookup_record_callback(flxCache *c, flxKey *pattern, flxCacheEntry *e, void *userdata) { + g_assert(c); + g_assert(pattern); + g_assert(e); + + if (flx_record_equal_no_ttl(e->record, userdata)) + return e; + + return NULL; +} + flxCacheEntry *flx_cache_lookup_record(flxCache *c, flxRecord *r) { flxCacheEntry *e; + g_assert(c); g_assert(r); - for (e = flx_cache_lookup_key(c, r->key); e; e = e->by_name_next) - if (flx_record_equal_no_ttl(e->record, r)) - return e; - - return NULL; + return flx_cache_walk(c, r->key, lookup_record_callback, r); } static void next_expiry(flxCache *c, flxCacheEntry *e, guint percent); @@ -88,7 +127,7 @@ static void elapse_func(flxTimeEvent *t, void *userdata) { g_assert(e); if (e->state == FLX_CACHE_FINAL) { - remove_entry(e->cache, e, TRUE); + remove_entry(e->cache, e); g_message("Removing entry from cache due to expiration"); } else { guint percent = 0; @@ -121,8 +160,9 @@ static void elapse_func(flxTimeEvent *t, void *userdata) { g_message("Requesting cache entry update at %i%%.", percent); - /* Request a cache update */ - flx_interface_post_query(e->cache->interface, e->record->key, TRUE); + /* Request a cache update, if we are subscribed to this entry */ + if (flx_is_subscribed(e->cache->server, e->record->key)) + flx_interface_post_query(e->cache->interface, e->record->key, TRUE); /* Check again later */ next_expiry(e->cache, e, percent); @@ -189,13 +229,13 @@ void flx_cache_update(flxCache *c, flxRecord *r, gboolean unique, const flxAddre if (unique) { /* For unique records, remove all entries but one */ - while (e->by_name_next) - remove_entry(c, e->by_name_next, TRUE); + while (e->by_key_next) + remove_entry(c, e->by_key_next); } else { /* For non-unique record, look for exactly the same entry */ - for (; e; e = e->by_name_next) + for (; e; e = e->by_key_next) if (flx_record_equal_no_ttl(e->record, r)) break; } @@ -206,7 +246,7 @@ void flx_cache_update(flxCache *c, flxRecord *r, gboolean unique, const flxAddre /* g_message("found matching cache entry"); */ /* We are the first in the linked list so let's replace the hash table key with the new one */ - if (e->by_name_prev == NULL) + if (e->by_key_prev == NULL) g_hash_table_replace(c->hash_table, r->key, e); /* Notify subscribers */ @@ -226,9 +266,14 @@ void flx_cache_update(flxCache *c, flxRecord *r, gboolean unique, const flxAddre e->cache = c; e->time_event = NULL; e->record = flx_record_ref(r); - FLX_LLIST_PREPEND(flxCacheEntry, by_name, t, e); + + /* Append to hash table */ + FLX_LLIST_PREPEND(flxCacheEntry, by_key, t, e); g_hash_table_replace(c->hash_table, e->record->key, t); - + + /* Append to linked list */ + FLX_LLIST_PREPEND(flxCacheEntry, entry, c->entries, e); + /* Notify subscribers */ flx_subscription_notify(c->server, c->interface, e->record, FLX_SUBSCRIPTION_NEW); } @@ -240,14 +285,20 @@ void flx_cache_update(flxCache *c, flxRecord *r, gboolean unique, const flxAddre } } +static gpointer drop_key_callback(flxCache *c, flxKey *pattern, flxCacheEntry *e, gpointer userdata) { + g_assert(c); + g_assert(pattern); + g_assert(e); + + remove_entry(c, e); + return NULL; +} + void flx_cache_drop_key(flxCache *c, flxKey *k) { - flxCacheEntry *e; - g_assert(c); g_assert(k); - while ((e = flx_cache_lookup_key(c, k))) - remove_entry(c, e, TRUE); + flx_cache_walk(c, k, drop_key_callback, NULL); } void flx_cache_drop_record(flxCache *c, flxRecord *r) { @@ -257,7 +308,7 @@ void flx_cache_drop_record(flxCache *c, flxRecord *r) { g_assert(r); if ((e = flx_cache_lookup_record(c, r))) - remove_entry(c, e, TRUE); + remove_entry(c, e); } static void func(gpointer key, gpointer data, gpointer userdata) { @@ -277,3 +328,19 @@ void flx_cache_dump(flxCache *c, FILE *f) { fprintf(f, ";;; CACHE DUMP FOLLOWS ;;;\n"); g_hash_table_foreach(c->hash_table, func, f); } + +gboolean flx_cache_entry_half_ttl(flxCache *c, flxCacheEntry *e) { + GTimeVal now; + guint age; + + g_assert(c); + g_assert(e); + + g_get_current_time(&now); + + age = flx_timeval_diff(&now, &e->timestamp)/1000000; + + g_message("age: %u, ttl/2: %u", age, e->record->ttl); + + return age >= e->record->ttl/2; +} diff --git a/cache.h b/cache.h index e7c8e7e..3b4eef6 100644 --- a/cache.h +++ b/cache.h @@ -32,7 +32,8 @@ struct flxCacheEntry { flxCacheEntryState state; flxTimeEvent *time_event; - FLX_LLIST_FIELDS(flxCacheEntry, by_name); + FLX_LLIST_FIELDS(flxCacheEntry, by_key); + FLX_LLIST_FIELDS(flxCacheEntry, entry); }; struct _flxCache { @@ -41,6 +42,8 @@ struct _flxCache { flxInterface *interface; GHashTable *hash_table; + + FLX_LLIST_HEAD(flxCacheEntry, entries); }; flxCache *flx_cache_new(flxServer *server, flxInterface *interface); @@ -51,9 +54,13 @@ flxCacheEntry *flx_cache_lookup_record(flxCache *c, flxRecord *r); void flx_cache_update(flxCache *c, flxRecord *r, gboolean unique, const flxAddress *a); -void flx_cache_drop_key(flxCache *c, flxKey *k); void flx_cache_drop_record(flxCache *c, flxRecord *r); void flx_cache_dump(flxCache *c, FILE *f); +typedef gpointer flxCacheWalkCallback(flxCache *c, flxKey *pattern, flxCacheEntry *e, gpointer userdata); +gpointer flx_cache_walk(flxCache *c, flxKey *pattern, flxCacheWalkCallback cb, gpointer userdata); + +gboolean flx_cache_entry_half_ttl(flxCache *c, flxCacheEntry *e); + #endif diff --git a/dns.c b/dns.c index 243c5a3..bed7344 100644 --- a/dns.c +++ b/dns.c @@ -409,29 +409,37 @@ flxKey* flx_dns_packet_consume_key(flxDnsPacket *p) { guint8* flx_dns_packet_append_key(flxDnsPacket *p, flxKey *k) { guint8 *t; + guint size; g_assert(p); g_assert(k); + size = p->size; + if (!(t = flx_dns_packet_append_name(p, k->name)) || !flx_dns_packet_append_uint16(p, k->type) || - !flx_dns_packet_append_uint16(p, k->class)) + !flx_dns_packet_append_uint16(p, k->class)) { + p->size = size; return NULL; + } return t; } guint8* flx_dns_packet_append_record(flxDnsPacket *p, flxRecord *r, gboolean cache_flush) { guint8 *t; + guint size; g_assert(p); g_assert(r); + size = p->size; + if (!(t = flx_dns_packet_append_name(p, r->key->name)) || !flx_dns_packet_append_uint16(p, r->key->type) || !flx_dns_packet_append_uint16(p, cache_flush ? (r->key->class | MDNS_CACHE_FLUSH) : (r->key->class &~ MDNS_CACHE_FLUSH)) || !flx_dns_packet_append_uint32(p, r->ttl)) - return NULL; + goto fail; switch (r->key->type) { @@ -445,7 +453,7 @@ guint8* flx_dns_packet_append_record(flxDnsPacket *p, flxRecord *r, gboolean cac if (!flx_dns_packet_append_uint16(p, strlen(ptr_name)+1) || !flx_dns_packet_append_name(p, ptr_name)) - return NULL; + goto fail; break; } @@ -460,7 +468,7 @@ guint8* flx_dns_packet_append_record(flxDnsPacket *p, flxRecord *r, gboolean cac if (!flx_dns_packet_append_uint16(p, strlen(name+6)+1+6) || !flx_dns_packet_append_bytes(p, r->data, 6) || !flx_dns_packet_append_name(p, name)) - return NULL; + goto fail; break; } @@ -468,8 +476,19 @@ guint8* flx_dns_packet_append_record(flxDnsPacket *p, flxRecord *r, gboolean cac default: if (!flx_dns_packet_append_uint16(p, r->size) || (r->size != 0 && !flx_dns_packet_append_bytes(p, r->data, r->size))) - return NULL; + goto fail; } return t; + + +fail: + p->size = size; + return NULL; +} + +gboolean flx_dns_packet_is_empty(flxDnsPacket *p) { + g_assert(p); + + return p->size <= FLX_DNS_PACKET_HEADER_SIZE; } diff --git a/dns.h b/dns.h index 0b6e750..c5d2fae 100644 --- a/dns.h +++ b/dns.h @@ -45,10 +45,10 @@ flxRecord* flx_dns_packet_consume_record(flxDnsPacket *p, gboolean *ret_cache_fl gconstpointer flx_dns_packet_get_rptr(flxDnsPacket *p); - - gint flx_dns_packet_skip(flxDnsPacket *p, guint length); +gboolean flx_dns_packet_is_empty(flxDnsPacket *p); + #define DNS_FIELD_ID 0 #define DNS_FIELD_FLAGS 1 #define DNS_FIELD_QDCOUNT 2 @@ -59,6 +59,7 @@ gint flx_dns_packet_skip(flxDnsPacket *p, guint length); #define DNS_FLAG_QR (1 << 15) #define DNS_FLAG_OPCODE (15 << 11) #define DNS_FLAG_RCODE (15) +#define DNS_FLAG_TC (1 << 9) #define DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \ (((guint16) !!qr << 15) | \ diff --git a/main.c b/main.c index 6f82d25..d5278c7 100644 --- a/main.c +++ b/main.c @@ -15,13 +15,13 @@ static gboolean send_timeout(gpointer data) { flxServer *flx = data; flxKey *k; - /* k = flx_key_new("cocaine.local.", FLX_DNS_CLASS_IN, FLX_DNS_TYPE_A); */ -/* flx_server_post_query(flx, 0, AF_UNSPEC, k); */ -/* flx_key_unref(k); */ + k = flx_key_new("ecstasy.local.", FLX_DNS_CLASS_IN, FLX_DNS_TYPE_TXT); + flx_server_post_query(flx, 0, AF_UNSPEC, k); + flx_key_unref(k); -/* k = flx_key_new("ecstasy.local.", FLX_DNS_CLASS_IN, FLX_DNS_TYPE_A); */ -/* flx_server_post_query(flx, 0, AF_INET, k); */ -/* flx_key_unref(k); */ + k = flx_key_new("ecstasy.local.", FLX_DNS_CLASS_IN, FLX_DNS_TYPE_A); + flx_server_post_query(flx, 0, AF_INET, k); + flx_key_unref(k); return FALSE; } @@ -58,7 +58,7 @@ int main(int argc, char *argv[]) { flx_server_add_text(flx, 0, 0, AF_UNSPEC, FALSE, NULL, "hallo"); -/* k = flx_key_new("_http._tcp.local.", FLX_DNS_CLASS_IN, FLX_DNS_TYPE_PTR); */ +/* k = flx_key_new("ecstasy.local.", FLX_DNS_CLASS_IN, FLX_DNS_TYPE_ANY); */ /* s = flx_subscription_new(flx, k, 0, AF_UNSPEC, subscription, NULL); */ /* flx_key_unref(k); */ diff --git a/psched.c b/psched.c index 162c1b2..138dd50 100644 --- a/psched.c +++ b/psched.c @@ -21,6 +21,7 @@ flxPacketScheduler *flx_packet_scheduler_new(flxServer *server, flxInterface *i) FLX_LLIST_HEAD_INIT(flxQueryJob, s->query_jobs); FLX_LLIST_HEAD_INIT(flxResponseJob, s->response_jobs); + FLX_LLIST_HEAD_INIT(flxKnownAnswer, s->known_answers); return s; } @@ -53,9 +54,11 @@ void flx_packet_scheduler_free(flxPacketScheduler *s) { flxQueryJob *qj; flxResponseJob *rj; flxTimeEvent *e; - + g_assert(s); + g_assert(!s->known_answers); + while ((qj = s->query_jobs)) query_job_free(s, qj); while ((rj = s->response_jobs)) @@ -64,6 +67,26 @@ void flx_packet_scheduler_free(flxPacketScheduler *s) { g_free(s); } +static gpointer known_answer_walk_callback(flxCache *c, flxKey *pattern, flxCacheEntry *e, gpointer userdata) { + flxPacketScheduler *s = userdata; + flxKnownAnswer *ka; + + g_assert(c); + g_assert(pattern); + g_assert(e); + g_assert(s); + + if (flx_cache_entry_half_ttl(c, e)) + return NULL; + + ka = g_new0(flxKnownAnswer, 1); + ka->scheduler = s; + ka->record = flx_record_ref(e->record); + + FLX_LLIST_PREPEND(flxKnownAnswer, known_answer, s->known_answers, ka); + return NULL; +} + static guint8* packet_add_query_job(flxPacketScheduler *s, flxDnsPacket *p, flxQueryJob *qj) { guint8 *d; @@ -81,11 +104,49 @@ static guint8* packet_add_query_job(flxPacketScheduler *s, flxDnsPacket *p, flxQ flx_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 */ + flx_cache_walk(s->interface->cache, qj->key, known_answer_walk_callback, s); } return d; } - + +static void append_known_answers_and_send(flxPacketScheduler *s, flxDnsPacket *p) { + flxKnownAnswer *ka; + guint n; + g_assert(s); + g_assert(p); + + n = 0; + + while ((ka = s->known_answers)) { + + while (!flx_dns_packet_append_record(p, ka->record, FALSE)) { + + g_assert(!flx_dns_packet_is_empty(p)); + + flx_dns_packet_set_field(p, DNS_FIELD_FLAGS, flx_dns_packet_get_field(p, DNS_FIELD_FLAGS) | DNS_FLAG_TC); + flx_dns_packet_set_field(p, DNS_FIELD_ANCOUNT, n); + flx_interface_send_packet(s->interface, p); + flx_dns_packet_free(p); + + p = flx_dns_packet_new_query(s->interface->hardware->mtu - 48); + n = 0; + } + + FLX_LLIST_REMOVE(flxKnownAnswer, known_answer, s->known_answers, ka); + flx_record_unref(ka->record); + g_free(ka); + + n++; + } + + flx_dns_packet_set_field(p, DNS_FIELD_ANCOUNT, n); + flx_interface_send_packet(s->interface, p); + flx_dns_packet_free(p); +} + static void query_elapse(flxTimeEvent *e, gpointer data) { flxQueryJob *qj = data; flxPacketScheduler *s; @@ -102,6 +163,8 @@ static void query_elapse(flxTimeEvent *e, gpointer data) { return; } + g_assert(!s->known_answers); + p = flx_dns_packet_new_query(s->interface->hardware->mtu - 48); d = packet_add_query_job(s, p, qj); g_assert(d); @@ -120,21 +183,9 @@ static void query_elapse(flxTimeEvent *e, gpointer data) { } flx_dns_packet_set_field(p, DNS_FIELD_QDCOUNT, n); - flx_interface_send_packet(s->interface, p); - flx_dns_packet_free(p); -} - -static flxQueryJob* look_for_query(flxPacketScheduler *s, flxKey *key) { - flxQueryJob *qj; - - g_assert(s); - g_assert(key); - - for (qj = s->query_jobs; qj; qj = qj->jobs_next) - if (flx_key_equal(qj->key, key)) - return qj; - return NULL; + /* Now add known answers */ + append_known_answers_and_send(s, p); } flxQueryJob* query_job_new(flxPacketScheduler *s, flxKey *key) { @@ -162,18 +213,22 @@ void flx_packet_scheduler_post_query(flxPacketScheduler *s, flxKey *key, gboolea g_assert(key); flx_elapse_time(&tv, immediately ? 0 : FLX_QUERY_DEFER_MSEC, 0); - - if ((qj = look_for_query(s, key))) { - glong d = flx_timeval_diff(&tv, &qj->delivery); - /* Duplicate questions suppression */ - if (d >= 0 && d <= FLX_QUERY_HISTORY_MSEC*1000) { - g_message("WARNING! DUPLICATE QUERY SUPPRESSION ACTIVE!"); - return; - } + for (qj = s->query_jobs; qj; qj = qj->jobs_next) - query_job_free(s, qj); - } + if (flx_key_equal(qj->key, key)) { + + glong d = flx_timeval_diff(&tv, &qj->delivery); + + /* Duplicate questions suppression */ + if (d >= 0 && d <= FLX_QUERY_HISTORY_MSEC*1000) { + g_message("WARNING! DUPLICATE QUERY SUPPRESSION ACTIVE!"); + return; + } + + query_job_free(s, qj); + break; + } qj = query_job_new(s, key); qj->delivery = tv; @@ -290,6 +345,8 @@ void flx_packet_scheduler_post_response(flxPacketScheduler *s, flxRecord *record g_assert(s); g_assert(record); + g_assert(!flx_key_is_pattern(record->key)); + flx_elapse_time(&tv, immediately ? 0 : FLX_RESPONSE_DEFER_MSEC, immediately ? 0 : FLX_RESPONSE_JITTER_MSEC); /* Don't send out duplicates */ diff --git a/psched.h b/psched.h index c312ce8..3a99ed1 100644 --- a/psched.h +++ b/psched.h @@ -4,6 +4,7 @@ typedef struct _flxQueryJob flxQueryJob; typedef struct _flxResponseJob flxResponseJob; typedef struct _flxPacketScheduler flxPacketScheduler; +typedef struct _flxKnownAnswer flxKnownAnswer; #include "timeeventq.h" #include "rr.h" @@ -28,6 +29,13 @@ struct _flxResponseJob { FLX_LLIST_FIELDS(flxResponseJob, jobs); }; +struct _flxKnownAnswer { + flxPacketScheduler *scheduler; + flxRecord *record; + + FLX_LLIST_FIELDS(flxKnownAnswer, known_answer); +}; + struct _flxPacketScheduler { flxServer *server; @@ -35,6 +43,7 @@ struct _flxPacketScheduler { FLX_LLIST_HEAD(flxQueryJob, query_jobs); FLX_LLIST_HEAD(flxResponseJob, response_jobs); + FLX_LLIST_HEAD(flxKnownAnswer, known_answers); }; flxPacketScheduler *flx_packet_scheduler_new(flxServer *server, flxInterface *i); diff --git a/rr.c b/rr.c index 294d409..600e721 100644 --- a/rr.c +++ b/rr.c @@ -224,9 +224,31 @@ gboolean flx_key_equal(const flxKey *a, const flxKey *b) { /* g_message("equal: %p %p", a, b); */ - return strcmp(a->name, b->name) == 0 && a->type == b->type && a->class == b->class; + return strcmp(a->name, b->name) == 0 && + a->type == b->type && + a->class == b->class; } +gboolean flx_key_pattern_match(const flxKey *pattern, const flxKey *k) { + g_assert(pattern); + g_assert(k); + +/* g_message("equal: %p %p", a, b); */ + + g_assert(!flx_key_is_pattern(k)); + + return strcmp(pattern->name, k->name) == 0 && + (pattern->type == k->type || pattern->type == FLX_DNS_TYPE_ANY) && + pattern->class == k->class; +} + +gboolean flx_key_is_pattern(const flxKey *k) { + g_assert(k); + + return k->type == FLX_DNS_TYPE_ANY; +} + + guint flx_key_hash(const flxKey *k) { g_assert(k); diff --git a/rr.h b/rr.h index f17c36f..65abb1d 100644 --- a/rr.h +++ b/rr.h @@ -13,7 +13,8 @@ enum { FLX_DNS_TYPE_MX = 0x0F, FLX_DNS_TYPE_TXT = 0x10, FLX_DNS_TYPE_AAAA = 0x1C, - FLX_DNS_TYPE_SRV = 0x21 + FLX_DNS_TYPE_SRV = 0x21, + FLX_DNS_TYPE_ANY = 0xFF }; enum { @@ -42,7 +43,11 @@ flxKey *flx_key_new(const gchar *name, guint16 class, guint16 type); flxKey *flx_key_ref(flxKey *k); void flx_key_unref(flxKey *k); -gboolean flx_key_equal(const flxKey *a, const flxKey *b); +gboolean flx_key_equal(const flxKey *a, const flxKey *b); /* Treat FLX_DNS_CLASS_ANY like any other type */ +gboolean flx_key_pattern_match(const flxKey *pattern, const flxKey *k); /* If pattern.type is FLX_DNS_CLASS_ANY, k.type is ignored */ + +gboolean flx_key_is_pattern(const flxKey *k); + guint flx_key_hash(const flxKey *k); flxRecord *flx_record_new(flxKey *k, gconstpointer data, guint16 size, guint32 ttl); diff --git a/server.c b/server.c index 6b3c2dd..ecf4457 100644 --- a/server.c +++ b/server.c @@ -24,9 +24,22 @@ static void handle_query_key(flxServer *s, flxKey *k, flxInterface *i, const flx flx_packet_scheduler_incoming_query(i->scheduler, k); - for (e = g_hash_table_lookup(s->rrset_by_key, k); e; e = e->by_key_next) - if (flx_interface_match(i, e->interface, e->protocol)) - flx_interface_post_response(i, e->record, FALSE); + if (k->type == FLX_DNS_TYPE_ANY) { + + /* Handle ANY query */ + + for (e = s->entries; e; e = e->entry_next) + if (flx_key_pattern_match(k, e->record->key)) + if (flx_interface_match(i, e->interface, e->protocol)) + flx_interface_post_response(i, e->record, FALSE); + } else { + + /* Handle all other queries */ + + for (e = g_hash_table_lookup(s->rrset_by_key, k); e; e = e->by_key_next) + if (flx_interface_match(i, e->interface, e->protocol)) + flx_interface_post_response(i, e->record, FALSE); + } } static void handle_query(flxServer *s, flxDnsPacket *p, flxInterface *i, const flxAddress *a) { @@ -69,13 +82,15 @@ static void handle_response(flxServer *s, flxDnsPacket *p, flxInterface *i, cons return; } - g_message("Handling response: %s", txt = flx_record_to_string(record)); - g_free(txt); - - flx_cache_update(i->cache, record, cache_flush, a); - - flx_packet_scheduler_incoming_response(i->scheduler, record); - flx_record_unref(record); + if (record->key->type != FLX_DNS_TYPE_ANY) { + g_message("Handling response: %s", txt = flx_record_to_string(record)); + g_free(txt); + + flx_cache_update(i->cache, record, cache_flush, a); + + flx_packet_scheduler_incoming_response(i->scheduler, record); + flx_record_unref(record); + } } } @@ -348,6 +363,8 @@ void flx_server_add( g_assert(s); g_assert(r); + g_assert(r->key->type != FLX_DNS_TYPE_ANY); + e = g_new(flxServerEntry, 1); e->record = flx_record_ref(r); e->id = id; diff --git a/subscribe.c b/subscribe.c index 7fd264d..abd1620 100644 --- a/subscribe.c +++ b/subscribe.c @@ -20,16 +20,39 @@ static void elapse(flxTimeEvent *e, void *userdata) { flx_time_event_queue_update(s->server->time_event_queue, s->time_event, &tv); } -static void scan_cache_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) { +struct cbdata { + flxSubscription *subscription; + flxInterface *interface; +}; + +static gpointer scan_cache_callback(flxCache *c, flxKey *pattern, flxCacheEntry *e, gpointer userdata) { + struct cbdata *cbdata = userdata; + + g_assert(c); + g_assert(pattern); + g_assert(e); + g_assert(cbdata); + + cbdata->subscription->callback( + cbdata->subscription, + e->record, + cbdata->interface->hardware->index, + cbdata->interface->protocol, + FLX_SUBSCRIPTION_NEW, + cbdata->subscription->userdata); + + return NULL; +} + +static void scan_interface_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) { flxSubscription *s = userdata; - flxCacheEntry *e; + struct cbdata cbdata = { s, i }; g_assert(m); g_assert(i); g_assert(s); - for (e = flx_cache_lookup_key(i->cache, s->key); e; e = e->by_name_next) - s->callback(s, e->record, i->hardware->index, i->protocol, FLX_SUBSCRIPTION_NEW, s->userdata); + flx_cache_walk(i->cache, s->key, scan_cache_callback, &cbdata); } flxSubscription *flx_subscription_new(flxServer *server, flxKey *key, gint interface, guchar protocol, flxSubscriptionCallback callback, gpointer userdata) { @@ -40,6 +63,8 @@ flxSubscription *flx_subscription_new(flxServer *server, flxKey *key, gint inter g_assert(key); g_assert(callback); + g_assert(!flx_key_is_pattern(key)); + s = g_new(flxSubscription, 1); s->server = server; s->key = flx_key_ref(key); @@ -63,7 +88,7 @@ flxSubscription *flx_subscription_new(flxServer *server, flxKey *key, gint inter g_hash_table_replace(server->subscription_hashtable, key, t); /* Scan the caches */ - flx_interface_monitor_walk(s->server->monitor, s->interface, s->protocol, scan_cache_callback, s); + flx_interface_monitor_walk(s->server->monitor, s->interface, s->protocol, scan_interface_callback, s); return s; } @@ -91,6 +116,7 @@ void flx_subscription_free(flxSubscription *s) { void flx_subscription_notify(flxServer *server, flxInterface *i, flxRecord *record, flxSubscriptionEvent event) { flxSubscription *s; + flxKey *pattern; g_assert(server); g_assert(record); @@ -98,5 +124,11 @@ void flx_subscription_notify(flxServer *server, flxInterface *i, flxRecord *reco for (s = g_hash_table_lookup(server->subscription_hashtable, record->key); s; s = s->by_key_next) if (flx_interface_match(i, s->interface, s->protocol)) s->callback(s, record, i->hardware->index, i->protocol, event, s->userdata); - +} + +gboolean flx_is_subscribed(flxServer *server, flxKey *k) { + g_assert(server); + g_assert(k); + + return !!g_hash_table_lookup(server->subscription_hashtable, k); } diff --git a/subscribe.h b/subscribe.h index 75818c7..6520433 100644 --- a/subscribe.h +++ b/subscribe.h @@ -36,4 +36,6 @@ void flx_subscription_free(flxSubscription *s); void flx_subscription_notify(flxServer *s, flxInterface *i, flxRecord *record, flxSubscriptionEvent event); +gboolean flx_is_subscribed(flxServer *s, flxKey *k); + #endif diff --git a/todo b/todo index 5576c7e..ab2519b 100644 --- a/todo +++ b/todo @@ -1,12 +1,17 @@ todo: -* Unicast responses/queries -* Known-Answer suppression -* Truncation * Probing/Conflict resolution -* Legacy unicast * uniqueness * defend our entries on incoming goodbye +* Unicast responses/queries +* Legacy unicast + +* Known-Answer suppression server part +* Truncation + done: * really send goodbye packets +* refresh subscribed records only +* FLX_DNS_TYPE_ANY support +* Known-Answer suppression client part -- 2.39.5