+#include "subscribe.h"
+
+static void free_entry(flxServer*s, flxEntry *e) {
+ flxEntry *t;
+
+ g_assert(s);
+ g_assert(e);
+
+ flx_goodbye_entry(s, e, TRUE);
+
+ /* Remove from linked list */
+ FLX_LLIST_REMOVE(flxEntry, entries, s->entries, e);
+
+ /* Remove from hash table indexed by name */
+ t = g_hash_table_lookup(s->entries_by_key, e->record->key);
+ FLX_LLIST_REMOVE(flxEntry, by_key, t, e);
+ if (t)
+ g_hash_table_replace(s->entries_by_key, t->record->key, t);
+ else
+ g_hash_table_remove(s->entries_by_key, e->record->key);
+
+ /* Remove from associated group */
+ if (e->group)
+ FLX_LLIST_REMOVE(flxEntry, by_group, e->group->entries, e);
+
+ flx_record_unref(e->record);
+ g_free(e);
+}
+
+static void free_group(flxServer *s, flxEntryGroup *g) {
+ g_assert(s);
+ g_assert(g);
+
+ while (g->entries)
+ free_entry(s, g->entries);
+
+ FLX_LLIST_REMOVE(flxEntryGroup, groups, s->groups, g);
+ g_free(g);
+}
+
+static void cleanup_dead(flxServer *s) {
+ flxEntryGroup *g, *ng;
+ flxEntry *e, *ne;
+ g_assert(s);
+
+
+ if (s->need_group_cleanup) {
+ for (g = s->groups; g; g = ng) {
+ ng = g->groups_next;
+
+ if (g->dead)
+ free_group(s, g);
+ }
+
+ s->need_group_cleanup = FALSE;
+ }
+
+ if (s->need_entry_cleanup) {
+ for (e = s->entries; e; e = ne) {
+ ne = e->entries_next;
+
+ if (e->dead)
+ free_entry(s, e);
+ }
+
+ s->need_entry_cleanup = FALSE;
+ }
+}
+
+static void handle_query_key(flxServer *s, flxKey *k, flxInterface *i, const flxAddress *a) {
+ flxEntry *e;
+ gchar *txt;
+
+ g_assert(s);
+ g_assert(k);
+ g_assert(i);
+ g_assert(a);
+
+ g_message("Handling query: %s", txt = flx_key_to_string(k));
+ g_free(txt);
+
+ flx_packet_scheduler_incoming_query(i->scheduler, k);
+
+ if (k->type == FLX_DNS_TYPE_ANY) {
+
+ /* Handle ANY query */
+
+ for (e = s->entries; e; e = e->entries_next)
+ if (!e->dead && flx_key_pattern_match(k, e->record->key) && flx_entry_registered(s, e, i))
+ flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, FALSE);
+ } else {
+
+ /* Handle all other queries */
+
+ for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
+ if (!e->dead && flx_entry_registered(s, e, i))
+ flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, FALSE);
+ }
+}
+
+static void withdraw_entry(flxServer *s, flxEntry *e) {
+ g_assert(s);
+ g_assert(e);
+
+ e->dead = TRUE;
+ s->need_entry_cleanup = TRUE;
+
+ flx_goodbye_entry(s, e, FALSE);
+
+ if (e->group)
+ flx_entry_group_run_callback(e->group, FLX_ENTRY_GROUP_COLLISION);
+}
+
+static void incoming_probe(flxServer *s, flxRecord *record, flxInterface *i) {
+ flxEntry *e, *n;
+ gchar *t;
+
+ g_assert(s);
+ g_assert(record);
+ g_assert(i);
+
+ t = flx_record_to_string(record);
+
+ for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
+ n = e->by_key_next;
+
+ if (e->dead || !flx_record_equal_no_ttl(record, e->record))
+ continue;
+
+ if (flx_entry_registering(s, e, i)) {
+
+ if (flx_record_lexicographical_compare(record, e->record) > 0) {
+ withdraw_entry(s, e);
+ g_message("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
+ } else
+ g_message("Recieved conflicting probe [%s]. Local host won.", t);
+ }
+ }
+
+ g_free(t);
+}
+
+static void handle_query(flxServer *s, flxDnsPacket *p, flxInterface *i, const flxAddress *a) {
+ guint n;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(i);
+ g_assert(a);
+
+ /* Handle the questions */
+ for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_QDCOUNT); n > 0; n --) {
+ flxKey *key;
+
+ if (!(key = flx_dns_packet_consume_key(p))) {
+ g_warning("Packet too short (1)");
+ return;
+ }
+
+ handle_query_key(s, key, i, a);
+ flx_key_unref(key);
+ }
+
+ /* Known Answer Suppresion */
+ for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT); n > 0; n --) {
+ flxRecord *record;
+ gboolean unique = FALSE;
+
+ if (!(record = flx_dns_packet_consume_record(p, &unique))) {
+ g_warning("Packet too short (2)");
+ return;
+ }
+
+ flx_packet_scheduler_incoming_known_answer(i->scheduler, record, a);
+ flx_record_unref(record);
+ }
+
+ /* Probe record */
+ for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_NSCOUNT); n > 0; n --) {
+ flxRecord *record;
+ gboolean unique = FALSE;
+
+ if (!(record = flx_dns_packet_consume_record(p, &unique))) {
+ g_warning("Packet too short (3)");
+ return;
+ }
+
+ if (record->key->type != FLX_DNS_TYPE_ANY)
+ incoming_probe(s, record, i);
+
+ flx_record_unref(record);
+ }
+}
+
+static gboolean handle_conflict(flxServer *s, flxInterface *i, flxRecord *record, const flxAddress *a) {
+ gboolean valid = TRUE;
+ flxEntry *e, *n;
+ gchar *t;
+
+ g_assert(s);
+ g_assert(i);
+ g_assert(record);
+
+ t = flx_record_to_string(record);
+
+ for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
+ n = e->by_key_next;
+
+ if (e->dead)
+ continue;
+
+ if (flx_entry_registered(s, e, i)) {
+
+ gboolean equal = flx_record_equal_no_ttl(record, e->record);
+
+ /* Check whether there is a unique record conflict */
+ if (!equal && (e->flags & FLX_ENTRY_UNIQUE)) {
+
+ /* The lexicographically later data wins. */
+ if (flx_record_lexicographical_compare(record, e->record) > 0) {
+ withdraw_entry(s, e);
+ g_message("Recieved conflicting record [%s]. Local host lost. Withdrawing.", t);
+ } else {
+ /* Tell the other host that our entry is lexicographically later */
+ valid = FALSE;
+ flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, TRUE);
+ g_message("Recieved conflicting record [%s]. Local host won. Refreshing.", t);
+ }
+
+ /* Check wheter there is a TTL conflict */
+ } else if (equal && record->ttl <= e->record->ttl/2) {
+ /* Correct the TTL */
+ valid = FALSE;
+ flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, TRUE);
+ g_message("Recieved record with bad TTL [%s]. Refreshing.", t);
+ }
+ }
+ }
+
+ g_free(t);
+
+ return valid;
+}
+
+static void handle_response(flxServer *s, flxDnsPacket *p, flxInterface *i, const flxAddress *a) {
+ guint n;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(i);
+ g_assert(a);
+
+ for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT) +
+ flx_dns_packet_get_field(p, FLX_DNS_FIELD_ARCOUNT); n > 0; n--) {
+ flxRecord *record;
+ gboolean cache_flush = FALSE;
+ gchar *txt;
+
+ if (!(record = flx_dns_packet_consume_record(p, &cache_flush))) {
+ g_warning("Packet too short (4)");
+ return;
+ }
+
+ if (record->key->type != FLX_DNS_TYPE_ANY) {
+
+ g_message("Handling response: %s", txt = flx_record_to_string(record));
+ g_free(txt);
+
+ if (handle_conflict(s, i, record, a)) {
+ flx_cache_update(i->cache, record, cache_flush, a);
+ flx_packet_scheduler_incoming_response(i->scheduler, record);
+ }
+ }
+
+ flx_record_unref(record);
+ }
+}