#include <string.h>
+#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);
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);
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);
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;
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);
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;
}
/* 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 */
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);
}
}
}
+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) {
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) {
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;
+}
flxCacheEntryState state;
flxTimeEvent *time_event;
- FLX_LLIST_FIELDS(flxCacheEntry, by_name);
+ FLX_LLIST_FIELDS(flxCacheEntry, by_key);
+ FLX_LLIST_FIELDS(flxCacheEntry, entry);
};
struct _flxCache {
flxInterface *interface;
GHashTable *hash_table;
+
+ FLX_LLIST_HEAD(flxCacheEntry, entries);
};
flxCache *flx_cache_new(flxServer *server, flxInterface *interface);
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
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) {
if (!flx_dns_packet_append_uint16(p, strlen(ptr_name)+1) ||
!flx_dns_packet_append_name(p, ptr_name))
- return NULL;
+ goto fail;
break;
}
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;
}
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;
}
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
#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) | \
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;
}
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); */
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;
}
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))
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;
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;
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);
}
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) {
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;
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 */
typedef struct _flxQueryJob flxQueryJob;
typedef struct _flxResponseJob flxResponseJob;
typedef struct _flxPacketScheduler flxPacketScheduler;
+typedef struct _flxKnownAnswer flxKnownAnswer;
#include "timeeventq.h"
#include "rr.h"
FLX_LLIST_FIELDS(flxResponseJob, jobs);
};
+struct _flxKnownAnswer {
+ flxPacketScheduler *scheduler;
+ flxRecord *record;
+
+ FLX_LLIST_FIELDS(flxKnownAnswer, known_answer);
+};
+
struct _flxPacketScheduler {
flxServer *server;
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);
/* 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);
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 {
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);
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) {
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);
+ }
}
}
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;
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) {
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);
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;
}
void flx_subscription_notify(flxServer *server, flxInterface *i, flxRecord *record, flxSubscriptionEvent event) {
flxSubscription *s;
+ flxKey *pattern;
g_assert(server);
g_assert(record);
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);
}
void flx_subscription_notify(flxServer *s, flxInterface *i, flxRecord *record, flxSubscriptionEvent event);
+gboolean flx_is_subscribed(flxServer *s, flxKey *k);
+
#endif
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