g_free(a);
}
-static void elapse_announce(flxTimeEvent *e, void *userdata) {
- flxAnnouncement *a = userdata;
+static void elapse_announce(flxTimeEvent *e, void *userdata);
+
+static void send_packet(flxAnnouncement *a) {
GTimeVal tv;
- gchar *t;
-
- g_assert(e);
g_assert(a);
- flx_interface_post_response(a->interface, NULL, a->entry->record, FALSE);
+/* g_message("%i -- %u", a->state, a->n_iteration); */
+
+ if (a->state == FLX_PROBING && a->n_iteration >= 1) {
+ flx_interface_post_probe(a->interface, a->entry->record, FALSE);
+ } else if (a->state == FLX_ANNOUNCING && a->n_iteration >= 1)
+ flx_interface_post_response(a->interface, NULL, a->entry->record, a->entry->flags & FLX_SERVER_ENTRY_UNIQUE, TRUE);
+
+ a->n_iteration++;
- if (a->n_announced++ <= 8)
- a->sec_delay *= 2;
+ if (a->state == FLX_PROBING) {
- g_message("Announcement #%i on interface %s.%i for entry [%s]", a->n_announced, a->interface->hardware->name, a->interface->protocol, t = flx_record_to_string(a->entry->record));
- g_free(t);
+ if (a->n_iteration == 1)
+ flx_elapse_time(&tv, 0, 250);
+ else
+ flx_elapse_time(&tv, 250, 0);
+
+ /* Probing done */
+ if (a->n_iteration >= 4) {
+ gchar *t;
+ g_message("Enough probes for record [%s]", t = flx_record_to_string(a->entry->record));
+ g_free(t);
+ a->state = FLX_ANNOUNCING;
+ a->n_iteration = 1;
+ }
+
+ } else if (a->state == FLX_ANNOUNCING) {
- if (a->n_announced >= 4) {
- g_message("Enough announcements for record [%s]", t = flx_record_to_string(a->entry->record));
- g_free(t);
- remove_announcement(a->server, a);
- } else {
flx_elapse_time(&tv, a->sec_delay*1000, FLX_ANNOUNCEMENT_JITTER_MSEC);
- flx_time_event_queue_update(a->server->time_event_queue, a->time_event, &tv);
+
+ if (a->n_iteration < 10)
+ a->sec_delay *= 2;
+
+ /* Announcing done */
+ if (a->n_iteration >= 4) {
+ gchar *t;
+ g_message("Enough announcements for record [%s]", t = flx_record_to_string(a->entry->record));
+ g_free(t);
+ remove_announcement(a->server, a);
+ return;
+ }
}
+
+ if (a->time_event)
+ flx_time_event_queue_update(a->server->time_event_queue, a->time_event, &tv);
+ else
+ a->time_event = flx_time_event_queue_add(a->server->time_event_queue, &tv, elapse_announce, a);
+}
+
+static void elapse_announce(flxTimeEvent *e, void *userdata) {
+ g_assert(e);
+
+ send_packet(userdata);
+}
+
+static flxAnnouncement *get_announcement(flxServer *s, flxServerEntry *e, flxInterface *i) {
+ flxAnnouncement *a;
+
+ g_assert(s);
+ g_assert(e);
+ g_assert(i);
+
+ for (a = e->announcements; a; a = a->by_entry_next)
+ if (a->interface == i)
+ return a;
+
+ return NULL;
}
static void new_announcement(flxServer *s, flxInterface *i, flxServerEntry *e) {
flxAnnouncement *a;
GTimeVal tv;
- gchar *t;
+ gchar *t;
g_assert(s);
g_assert(i);
g_assert(e);
- g_message("NEW ANNOUNCEMENT: %s.%i [%s]", i->hardware->name, i->protocol, t = flx_record_to_string(e->record));
- g_free(t);
+/* g_message("NEW ANNOUNCEMENT: %s.%i [%s]", i->hardware->name, i->protocol, t = flx_record_to_string(e->record)); */
+/* g_free(t); */
- if (!flx_interface_match(i, e->interface, e->protocol) || !i->announcing)
+ if (!flx_interface_match(i, e->interface, e->protocol) || !i->announcing || e->flags & FLX_SERVER_ENTRY_NOANNOUNCE)
+ return;
+
+ /* We don't want duplicate announcements */
+ if (get_announcement(s, e, i))
return;
- /* We don't want duplicates */
- for (a = e->announcements; a; a = a->by_entry_next)
- if (a->interface == i)
- return;
-
g_message("New announcement on interface %s.%i for entry [%s]", i->hardware->name, i->protocol, t = flx_record_to_string(e->record));
g_free(t);
-
- flx_interface_post_response(i, NULL, e->record, FALSE);
-
+
a = g_new(flxAnnouncement, 1);
a->server = s;
a->interface = i;
a->entry = e;
- a->n_announced = 1;
+
+ a->state = (e->flags & FLX_SERVER_ENTRY_UNIQUE) && !(e->flags & FLX_SERVER_ENTRY_NOPROBE) ? FLX_PROBING : FLX_ANNOUNCING;
+ a->n_iteration = 0;
a->sec_delay = 1;
+ a->time_event = NULL;
FLX_LLIST_PREPEND(flxAnnouncement, by_interface, i->announcements, a);
FLX_LLIST_PREPEND(flxAnnouncement, by_entry, e->announcements, a);
-
- flx_elapse_time(&tv, a->sec_delay*1000, FLX_ANNOUNCEMENT_JITTER_MSEC);
- a->time_event = flx_time_event_queue_add(s->time_event_queue, &tv, elapse_announce, a);
+
+ send_packet(a);
}
void flx_announce_interface(flxServer *s, flxInterface *i) {
if (!i->announcing)
return;
- g_message("ANNOUNCE INTERFACE");
+/* g_message("ANNOUNCE INTERFACE"); */
for (e = s->entries; e; e = e->entry_next)
new_announcement(s, i, e);
g_assert(s);
g_assert(e);
- g_message("ANNOUNCE ENTRY");
+/* g_message("ANNOUNCE ENTRY"); */
flx_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e);
}
+gboolean flx_entry_established(flxServer *s, flxServerEntry *e, flxInterface *i) {
+ flxAnnouncement *a;
+
+ g_assert(s);
+ g_assert(e);
+ g_assert(i);
+
+ if (!(e->flags & FLX_SERVER_ENTRY_UNIQUE) || (e->flags & FLX_SERVER_ENTRY_NOPROBE))
+ return TRUE;
+
+ if ((a = get_announcement(s, e, i)))
+ if (a->state == FLX_PROBING)
+ return FALSE;
+
+ return TRUE;
+}
+
+
static flxRecord *make_goodbye_record(flxRecord *r) {
gchar *t;
flxRecord *g;
return g;
}
+
+
+static void send_goodbye_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) {
+ flxServerEntry *e = userdata;
+ flxRecord *g;
+
+ g_assert(m);
+ g_assert(i);
+ g_assert(e);
+
+ if (!flx_interface_match(i, e->interface, e->protocol))
+ return;
+
+ if (e->flags & FLX_SERVER_ENTRY_NOANNOUNCE)
+ return;
+
+ if (!flx_entry_established(m->server, e, i))
+ return;
+
+ g = make_goodbye_record(e->record);
+ flx_interface_post_response(i, NULL, g, e->flags & FLX_SERVER_ENTRY_UNIQUE, TRUE);
+ flx_record_unref(g);
+}
void flx_goodbye_interface(flxServer *s, flxInterface *i, gboolean goodbye) {
g_assert(s);
g_assert(i);
- while (i->announcements)
- remove_announcement(s, i->announcements);
+ g_message("goodbye interface: %s.%u", i->hardware->name, i->protocol);
if (goodbye && flx_interface_relevant(i)) {
flxServerEntry *e;
for (e = s->entries; e; e = e->entry_next)
- if (flx_interface_match(i, e->interface, e->protocol)) {
- flxRecord *g = make_goodbye_record(e->record);
- flx_interface_post_response(i, NULL, g, TRUE);
- flx_record_unref(g);
- }
+ send_goodbye_callback(s->monitor, i, e);
}
+
+ while (i->announcements)
+ remove_announcement(s, i->announcements);
+
+ g_message("goodbye interface done: %s.%u", i->hardware->name, i->protocol);
+
}
void flx_goodbye_entry(flxServer *s, flxServerEntry *e, gboolean goodbye) {
g_assert(s);
g_assert(e);
+
+ g_message("goodbye entry: %p", e);
+ if (goodbye)
+ flx_interface_monitor_walk(s->monitor, 0, AF_UNSPEC, send_goodbye_callback, e);
+
while (e->announcements)
remove_announcement(s, e->announcements);
-
- if (goodbye) {
- flxRecord *g = make_goodbye_record(e->record);
- flx_server_post_response(s, e->interface, e->protocol, g);
- flx_record_unref(g);
- }
+
+ g_message("goodbye entry done: %p", e);
+
}
void flx_goodbye_all(flxServer *s, gboolean goodbye) {
g_assert(s);
+ g_message("goodbye all: %p", e);
+
for (e = s->entries; e; e = e->entry_next)
flx_goodbye_entry(s, e, goodbye);
+
+ g_message("goodbye all done: %p", e);
+
}
+
#include "server.h"
#include "timeeventq.h"
+typedef enum {
+ FLX_PROBING,
+ FLX_ANNOUNCING,
+} flxAnnouncementState;
+
struct _flxAnnouncement {
flxServer *server;
flxInterface *interface;
flxServerEntry *entry;
-
+
flxTimeEvent *time_event;
- guint n_announced;
+
+ flxAnnouncementState state;
+ guint n_iteration;
guint sec_delay;
FLX_LLIST_FIELDS(flxAnnouncement, by_interface);
void flx_announce_interface(flxServer *s, flxInterface *i);
void flx_announce_entry(flxServer *s, flxServerEntry *e);
+gboolean flx_entry_established(flxServer *s, flxServerEntry *e, flxInterface *i);
+
void flx_goodbye_interface(flxServer *s, flxInterface *i, gboolean send);
void flx_goodbye_entry(flxServer *s, flxServerEntry *e, gboolean send);
flxDnsPacket *p;
p = flx_dns_packet_new(max_size);
- flx_dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ flx_dns_packet_set_field(p, FLX_DNS_FIELD_FLAGS, FLX_DNS_FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
return p;
}
flxDnsPacket *p;
p = flx_dns_packet_new(max_size);
- flx_dns_packet_set_field(p, DNS_FIELD_FLAGS, DNS_FLAGS(1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
+ flx_dns_packet_set_field(p, FLX_DNS_FIELD_FLAGS, FLX_DNS_FLAGS(1, 0, 0, 0, 0, 0, 0, 0, 0, 0));
return p;
}
if (p->size < 12)
return -1;
- flags = flx_dns_packet_get_field(p, DNS_FIELD_FLAGS);
+ flags = flx_dns_packet_get_field(p, FLX_DNS_FIELD_FLAGS);
- if (flags & DNS_FLAG_OPCODE || flags & DNS_FLAG_RCODE)
+ if (flags & FLX_DNS_FLAG_OPCODE || flags & FLX_DNS_FLAG_RCODE)
return -1;
return 0;
gint flx_dns_packet_is_query(flxDnsPacket *p) {
g_assert(p);
- return !(flx_dns_packet_get_field(p, DNS_FIELD_FLAGS) & DNS_FLAG_QR);
+ return !(flx_dns_packet_get_field(p, FLX_DNS_FIELD_FLAGS) & FLX_DNS_FLAG_QR);
}
static gint consume_labels(flxDnsPacket *p, guint index, gchar *ret_name, guint l) {
if ((guint8*) flx_dns_packet_get_rptr(p) - (guint8*) start != rdlength)
goto fail;
- *ret_cache_flush = !!(class & MDNS_CACHE_FLUSH);
- class &= ~ MDNS_CACHE_FLUSH;
+ *ret_cache_flush = !!(class & FLX_DNS_CACHE_FLUSH);
+ class &= ~ FLX_DNS_CACHE_FLUSH;
r->ttl = ttl;
flx_dns_packet_consume_uint16(p, &class) < 0)
return NULL;
- class &= ~ MDNS_CACHE_FLUSH;
+ class &= ~ FLX_DNS_CACHE_FLUSH;
return flx_key_new(name, class, type);
}
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_uint16(p, cache_flush ? (r->key->class | FLX_DNS_CACHE_FLUSH) : (r->key->class &~ FLX_DNS_CACHE_FLUSH)) ||
!flx_dns_packet_append_uint32(p, r->ttl) ||
!(l = flx_dns_packet_append_uint16(p, 0)))
goto fail;
return p->size <= FLX_DNS_PACKET_HEADER_SIZE;
}
+
+guint flx_dns_packet_space(flxDnsPacket *p) {
+ g_assert(p);
+
+ g_assert(p->size <= p->max_size);
+
+ return p->max_size - p->size;
+}
gint flx_dns_packet_skip(flxDnsPacket *p, guint length);
gboolean flx_dns_packet_is_empty(flxDnsPacket *p);
+guint flx_dns_packet_space(flxDnsPacket *p);
-#define DNS_FIELD_ID 0
-#define DNS_FIELD_FLAGS 1
-#define DNS_FIELD_QDCOUNT 2
-#define DNS_FIELD_ANCOUNT 3
-#define DNS_FIELD_NSCOUNT 4
-#define DNS_FIELD_ARCOUNT 5
+#define FLX_DNS_FIELD_ID 0
+#define FLX_DNS_FIELD_FLAGS 1
+#define FLX_DNS_FIELD_QDCOUNT 2
+#define FLX_DNS_FIELD_ANCOUNT 3
+#define FLX_DNS_FIELD_NSCOUNT 4
+#define FLX_DNS_FIELD_ARCOUNT 5
-#define DNS_FLAG_QR (1 << 15)
-#define DNS_FLAG_OPCODE (15 << 11)
-#define DNS_FLAG_RCODE (15)
-#define DNS_FLAG_TC (1 << 9)
+#define FLX_DNS_FLAG_QR (1 << 15)
+#define FLX_DNS_FLAG_OPCODE (15 << 11)
+#define FLX_DNS_FLAG_RCODE (15)
+#define FLX_DNS_FLAG_TC (1 << 9)
-#define DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \
+#define FLX_DNS_FLAGS(qr, opcode, aa, tc, rd, ra, z, ad, cd, rcode) \
(((guint16) !!qr << 15) | \
((guint16) (opcode & 15) << 11) | \
((guint16) !!aa << 10) | \
((guint16) (rd & 15)))
-#define MDNS_CACHE_FLUSH 0x8000
-
#endif
#include "address.h"
#include "rr.h"
+typedef enum {
+ FLX_SERVER_ENTRY_NULL = 0,
+ FLX_SERVER_ENTRY_UNIQUE = 1,
+ FLX_SERVER_ENTRY_NOPROBE = 2,
+ FLX_SERVER_ENTRY_NOANNOUNCE = 4
+} flxServerEntryFlags;
+
flxServer *flx_server_new(GMainContext *c);
void flx_server_free(flxServer* s);
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
flxRecord *r);
void flx_server_add_ptr(
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
const gchar *name,
const gchar *dest);
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
const gchar *name,
flxAddress *a);
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
const gchar *name,
... /* text records, terminated by NULL */);
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
const gchar *name,
va_list va);
void flx_server_remove(flxServer *s, gint id);
void flx_server_post_query(flxServer *s, gint interface, guchar protocol, flxKey *key);
-void flx_server_post_response(flxServer *s, gint interface, guchar protocol, flxRecord *record);
+void flx_server_post_response(flxServer *s, gint interface, guchar protocol, flxRecord *record, gboolean flush_cache);
const flxRecord *flx_server_iterate(flxServer *s, gint id, void **state);
} else {
if (a->rr_id < 0) {
a->rr_id = flx_server_get_next_id(m->server);
- flx_server_add_address(m->server, a->rr_id, a->interface->hardware->index, AF_UNSPEC, FALSE, m->server->hostname, &a->address);
+ flx_server_add_address(m->server, a->rr_id, a->interface->hardware->index, AF_UNSPEC, 0, NULL, &a->address);
}
}
}
}
-void flx_interface_post_response(flxInterface *i, const flxAddress *a, flxRecord *record, gboolean immediately) {
+void flx_interface_post_response(flxInterface *i, const flxAddress *a, flxRecord *record, gboolean flush_cache, gboolean immediately) {
g_assert(i);
g_assert(record);
if (flx_interface_relevant(i))
- flx_packet_scheduler_post_response(i->scheduler, a, record, immediately);
+ flx_packet_scheduler_post_response(i->scheduler, a, record, flush_cache, immediately);
+}
+
+void flx_interface_post_probe(flxInterface *i, flxRecord *record, gboolean immediately) {
+ g_assert(i);
+ g_assert(record);
+
+ if (flx_interface_relevant(i))
+ flx_packet_scheduler_post_probe(i->scheduler, record, immediately);
}
void flx_dump_caches(flxInterfaceMonitor *m, FILE *f) {
void flx_interface_send_packet(flxInterface *i, flxDnsPacket *p);
void flx_interface_post_query(flxInterface *i, flxKey *k, gboolean immediately);
-void flx_interface_post_response(flxInterface *i, const flxAddress *a, flxRecord *rr, gboolean immediately);
+void flx_interface_post_probe(flxInterface *i, flxRecord *p, gboolean immediately);
+void flx_interface_post_response(flxInterface *i, const flxAddress *a, flxRecord *record, gboolean flush_cache, gboolean immediately);
void flx_dump_caches(flxInterfaceMonitor *m, FILE *f);
flxServer *flx = data;
flxKey *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_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 = flx_server_new(NULL);
- flx_server_add_text(flx, 0, 0, AF_UNSPEC, FALSE, NULL, "hallo", NULL);
+ flx_server_add_text(flx, 0, 0, AF_UNSPEC, FLX_SERVER_ENTRY_UNIQUE, NULL, "hallo", NULL);
flx_server_add_service(flx, 0, 0, AF_UNSPEC, "_http._tcp", "gurke", NULL, NULL, 80, "foo", NULL);
/* k = flx_key_new("ecstasy.local.", FLX_DNS_CLASS_IN, FLX_DNS_TYPE_ANY); */
#include "util.h"
#include "psched.h"
-#define FLX_QUERY_HISTORY_MSEC 700
+#define FLX_QUERY_HISTORY_MSEC 100
#define FLX_QUERY_DEFER_MSEC 100
#define FLX_RESPONSE_HISTORY_MSEC 700
#define FLX_RESPONSE_DEFER_MSEC 20
#define FLX_RESPONSE_JITTER_MSEC 100
+#define FLX_PROBE_DEFER_MSEC 100
flxPacketScheduler *flx_packet_scheduler_new(flxServer *server, flxInterface *i) {
flxPacketScheduler *s;
FLX_LLIST_HEAD_INIT(flxQueryJob, s->query_jobs);
FLX_LLIST_HEAD_INIT(flxResponseJob, s->response_jobs);
FLX_LLIST_HEAD_INIT(flxKnownAnswer, s->known_answers);
+ FLX_LLIST_HEAD_INIT(flxProbeJob, s->probe_jobs);
return s;
}
g_free(rj);
}
+static void probe_job_free(flxPacketScheduler *s, flxProbeJob *pj) {
+ g_assert(pj);
+
+ if (pj->time_event)
+ flx_time_event_queue_remove(pj->scheduler->server->time_event_queue, pj->time_event);
+
+ FLX_LLIST_REMOVE(flxProbeJob, jobs, s->probe_jobs, pj);
+
+ flx_record_unref(pj->record);
+ g_free(pj);
+}
+
void flx_packet_scheduler_free(flxPacketScheduler *s) {
flxQueryJob *qj;
flxResponseJob *rj;
+ flxProbeJob *pj;
flxTimeEvent *e;
g_assert(s);
query_job_free(s, qj);
while ((rj = s->response_jobs))
response_job_free(s, rj);
+ while ((pj = s->probe_jobs))
+ probe_job_free(s, pj);
g_free(s);
}
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_dns_packet_set_field(p, FLX_DNS_FIELD_FLAGS, flx_dns_packet_get_field(p, FLX_DNS_FIELD_FLAGS) | FLX_DNS_FLAG_TC);
+ flx_dns_packet_set_field(p, FLX_DNS_FIELD_ANCOUNT, n);
flx_interface_send_packet(s->interface, p);
flx_dns_packet_free(p);
n++;
}
- flx_dns_packet_set_field(p, DNS_FIELD_ANCOUNT, n);
+ flx_dns_packet_set_field(p, FLX_DNS_FIELD_ANCOUNT, n);
flx_interface_send_packet(s->interface, p);
flx_dns_packet_free(p);
}
n++;
}
- flx_dns_packet_set_field(p, DNS_FIELD_QDCOUNT, n);
+ flx_dns_packet_set_field(p, FLX_DNS_FIELD_QDCOUNT, n);
/* Now add known answers */
append_known_answers_and_send(s, p);
g_assert(p);
g_assert(rj);
- if ((d = flx_dns_packet_append_record(p, rj->record, FALSE))) {
+ if ((d = flx_dns_packet_append_record(p, rj->record, rj->flush_cache))) {
GTimeVal tv;
rj->done = 1;
n++;
}
- flx_dns_packet_set_field(p, DNS_FIELD_ANCOUNT, n);
+ flx_dns_packet_set_field(p, FLX_DNS_FIELD_ANCOUNT, n);
flx_interface_send_packet(s->interface, p);
flx_dns_packet_free(p);
}
rj->record = flx_record_ref(record);
rj->done = FALSE;
rj->time_event = NULL;
+ rj->address_valid = FALSE;
+ rj->flush_cache = FALSE;
FLX_LLIST_PREPEND(flxResponseJob, jobs, s->response_jobs, rj);
return rj;
}
-void flx_packet_scheduler_post_response(flxPacketScheduler *s, const flxAddress *a, flxRecord *record, gboolean immediately) {
+void flx_packet_scheduler_post_response(flxPacketScheduler *s, const flxAddress *a, flxRecord *record, gboolean flush_cache, gboolean immediately) {
flxResponseJob *rj;
GTimeVal tv;
gchar *t;
/* This job is no longer specific to a single querier, so
* make sure it isn't suppressed by known answer
* suppresion */
- rj->address_valid = FALSE;
+
+ if (rj->address_valid && (!a || flx_address_cmp(a, &rj->address) != 0))
+ rj->address_valid = FALSE;
+
+ rj->flush_cache = flush_cache;
return;
}
/* Create a new job and schedule it */
rj = response_job_new(s, record);
+ rj->flush_cache = flush_cache;
rj->delivery = tv;
rj->time_event = flx_time_event_queue_add(s->server->time_event_queue, &rj->delivery, response_elapse, rj);
g_assert(record);
g_assert(a);
- for (rj = s->response_jobs; rj; rj = rj->jobs_next)
- if (flx_record_equal_no_ttl(rj->record, record) &&
- rj->address_valid &&
- flx_address_cmp(&rj->address, a) &&
- record->ttl >= rj->record->ttl/2) {
+ for (rj = s->response_jobs; rj; rj = rj->jobs_next) {
+
+ g_assert(record->ttl > 0);
+ g_assert(rj->record->ttl/2);
+
+ if (flx_record_equal_no_ttl(rj->record, record))
+ if (rj->address_valid)
+ if (flx_address_cmp(&rj->address, a))
+ if (record->ttl >= rj->record->ttl/2) {
/* Let's suppress it */
response_job_free(s, rj);
break;
}
-
+ }
}
void flx_packet_scheduler_flush_responses(flxPacketScheduler *s) {
if (!rj->done)
send_response_packet(s, rj);
}
+
+static flxProbeJob* probe_job_new(flxPacketScheduler *s, flxRecord *record) {
+ flxProbeJob *pj;
+
+ g_assert(s);
+ g_assert(record);
+
+ pj = g_new(flxProbeJob, 1);
+ pj->scheduler = s;
+ pj->record = flx_record_ref(record);
+ pj->time_event = NULL;
+ pj->chosen = FALSE;
+
+ FLX_LLIST_PREPEND(flxProbeJob, jobs, s->probe_jobs, pj);
+
+ return pj;
+}
+
+static guint8* packet_add_probe_query(flxPacketScheduler *s, flxDnsPacket *p, flxProbeJob *pj) {
+ guint size;
+ guint8 *r;
+ flxKey *k;
+
+ g_assert(s);
+ g_assert(p);
+ g_assert(pj);
+
+ g_assert(!pj->chosen);
+
+ /* Estimate the size for this record */
+ size =
+ flx_key_get_estimate_size(pj->record->key) +
+ flx_record_get_estimate_size(pj->record);
+
+ /* Too large */
+ if (size > flx_dns_packet_space(p))
+ return NULL;
+
+ /* Create the probe query */
+ k = flx_key_new(pj->record->key->name, pj->record->key->class, FLX_DNS_TYPE_ANY);
+ r = flx_dns_packet_append_key(p, k);
+
+ /* Mark this job for addition to the packet */
+ pj->chosen = TRUE;
+
+ /* Scan for more jobs whith matching key pattern */
+ for (pj = s->probe_jobs; pj; pj = pj->jobs_next) {
+ if (pj->chosen)
+ continue;
+
+ /* Does the record match the probe? */
+ if (k->class != pj->record->key->class || strcmp(k->name, pj->record->key->name))
+ continue;
+
+ /* This job wouldn't fit in */
+ if (flx_record_get_estimate_size(pj->record) > flx_dns_packet_space(p))
+ break;
+
+ /* Mark this job for addition to the packet */
+ pj->chosen = TRUE;
+ }
+
+ flx_key_unref(k);
+
+ return r;
+}
+
+static void probe_elapse(flxTimeEvent *e, gpointer data) {
+ flxProbeJob *pj = data, *next;
+ flxPacketScheduler *s;
+ flxDnsPacket *p;
+ guint n;
+ guint8 *d;
+
+ g_assert(pj);
+ s = pj->scheduler;
+
+ p = flx_dns_packet_new_query(s->interface->hardware->mtu - 48);
+
+ /* Add the import probe */
+ if (!packet_add_probe_query(s, p, pj)) {
+ g_warning("Record too large!");
+ flx_dns_packet_free(p);
+ return;
+ }
+
+ n = 1;
+
+ /* Try to fill up packet with more probes, if available */
+ for (pj = s->probe_jobs; pj; pj = pj->jobs_next) {
+
+ if (pj->chosen)
+ continue;
+
+ if (!packet_add_probe_query(s, p, pj))
+ break;
+
+ n++;
+ }
+
+ flx_dns_packet_set_field(p, FLX_DNS_FIELD_QDCOUNT, n);
+
+ n = 0;
+
+ /* Now add the chosen records to the authorative section */
+ for (pj = s->probe_jobs; pj; pj = next) {
+
+ next = pj->jobs_next;
+
+ if (!pj->chosen)
+ continue;
+
+ if (!flx_dns_packet_append_record(p, pj->record, TRUE)) {
+ g_warning("Bad probe size estimate!");
+
+ /* Unmark all following jobs */
+ for (; pj; pj = pj->jobs_next)
+ pj->chosen = FALSE;
+
+ break;
+ }
+
+ probe_job_free(s, pj);
+ n ++;
+ }
+
+ flx_dns_packet_set_field(p, FLX_DNS_FIELD_NSCOUNT, n);
+
+ /* Send it now */
+ flx_interface_send_packet(s->interface, p);
+ flx_dns_packet_free(p);
+}
+
+void flx_packet_scheduler_post_probe(flxPacketScheduler *s, flxRecord *record, gboolean immediately) {
+ flxProbeJob *pj;
+ GTimeVal tv;
+
+ g_assert(s);
+ g_assert(record);
+ g_assert(!flx_key_is_pattern(record->key));
+
+ flx_elapse_time(&tv, immediately ? 0 : FLX_PROBE_DEFER_MSEC, 0);
+
+ /* No duplication check here... */
+ /* Create a new job and schedule it */
+ pj = probe_job_new(s, record);
+ pj->delivery = tv;
+ pj->time_event = flx_time_event_queue_add(s->server->time_event_queue, &pj->delivery, probe_elapse, pj);
+}
typedef struct _flxResponseJob flxResponseJob;
typedef struct _flxPacketScheduler flxPacketScheduler;
typedef struct _flxKnownAnswer flxKnownAnswer;
+typedef struct _flxProbeJob flxProbeJob;
#include "timeeventq.h"
#include "rr.h"
gboolean address_valid;
gboolean done;
GTimeVal delivery;
+ gboolean flush_cache;
FLX_LLIST_FIELDS(flxResponseJob, jobs);
};
FLX_LLIST_FIELDS(flxKnownAnswer, known_answer);
};
+struct _flxProbeJob {
+ flxPacketScheduler *scheduler;
+ flxTimeEvent *time_event;
+ flxRecord *record;
+
+ gboolean chosen; /* Use for packet assembling */
+ GTimeVal delivery;
+
+ FLX_LLIST_FIELDS(flxProbeJob, jobs);
+};
+
struct _flxPacketScheduler {
flxServer *server;
FLX_LLIST_HEAD(flxQueryJob, query_jobs);
FLX_LLIST_HEAD(flxResponseJob, response_jobs);
FLX_LLIST_HEAD(flxKnownAnswer, known_answers);
+ FLX_LLIST_HEAD(flxProbeJob, probe_jobs);
};
flxPacketScheduler *flx_packet_scheduler_new(flxServer *server, flxInterface *i);
void flx_packet_scheduler_free(flxPacketScheduler *s);
void flx_packet_scheduler_post_query(flxPacketScheduler *s, flxKey *key, gboolean immediately);
-void flx_packet_scheduler_post_response(flxPacketScheduler *s, const flxAddress *a, flxRecord *record, gboolean immediately);
+void flx_packet_scheduler_post_response(flxPacketScheduler *s, const flxAddress *a, flxRecord *record, gboolean flush_cache, gboolean immediately);
+void flx_packet_scheduler_post_probe(flxPacketScheduler *s, flxRecord *record, gboolean immediately);
void flx_packet_scheduler_incoming_query(flxPacketScheduler *s, flxKey *key);
void flx_packet_scheduler_incoming_response(flxPacketScheduler *s, flxRecord *record);
}
const gchar *flx_dns_class_to_string(guint16 class) {
+ if (class & FLX_DNS_CACHE_FLUSH)
+ return "FLUSH";
+
if (class == FLX_DNS_CLASS_IN)
return "IN";
return "TXT";
case FLX_DNS_TYPE_SRV:
return "SRV";
+ case FLX_DNS_TYPE_ANY:
+ return "ANY";
default:
return NULL;
}
return copy;
}
+
+
+guint flx_key_get_estimate_size(flxKey *k) {
+ g_assert(k);
+
+ return strlen(k->name)+1+4;
+}
+
+guint flx_record_get_estimate_size(flxRecord *r) {
+ guint n;
+ g_assert(r);
+
+ n = flx_key_get_estimate_size(r->key) + 4 + 2;
+
+ switch (r->key->type) {
+ case FLX_DNS_TYPE_PTR:
+ case FLX_DNS_TYPE_CNAME:
+ n += strlen(r->data.ptr.name) + 1;
+ break;
+
+ case FLX_DNS_TYPE_SRV:
+ n += 6 + strlen(r->data.srv.name) + 1;
+ break;
+
+ case FLX_DNS_TYPE_HINFO:
+ n += strlen(r->data.hinfo.os) + 1 + strlen(r->data.hinfo.cpu) + 1;
+ break;
+
+ case FLX_DNS_TYPE_TXT:
+ n += flx_string_list_serialize(r->data.txt.string_list, NULL, 0);
+ break;
+
+ case FLX_DNS_TYPE_A:
+ n += sizeof(flxIPv4Address);
+ break;
+
+ case FLX_DNS_TYPE_AAAA:
+ n += sizeof(flxIPv6Address);
+ break;
+
+ default:
+ n += r->data.generic.size;
+ }
+
+ return n;
+}
};
enum {
- FLX_DNS_CLASS_IN = 0x01
+ FLX_DNS_CLASS_IN = 0x01,
+ FLX_DNS_CACHE_FLUSH = 0x8000
};
#define FLX_DEFAULT_TTL (120*60)
flxRecord *flx_record_copy(flxRecord *r);
+/* returns a maximum estimate for the space that is needed to store
+ * this key in a DNS packet */
+guint flx_key_get_estimate_size(flxKey *k);
+
+/* ditto */
+guint flx_record_get_estimate_size(flxRecord *r);
+
+
#endif
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, a, e->record, FALSE);
+ if (flx_interface_match(i, e->interface, e->protocol) && flx_entry_established(s, e, i))
+ flx_interface_post_response(i, a, e->record, e->flags & FLX_SERVER_ENTRY_UNIQUE, 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, a, e->record, FALSE);
+ if (flx_interface_match(i, e->interface, e->protocol) && flx_entry_established(s, e, i))
+ flx_interface_post_response(i, a, e->record, e->flags & FLX_SERVER_ENTRY_UNIQUE, FALSE);
}
}
g_assert(i);
g_assert(a);
- for (n = flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT); n > 0; n --) {
+ 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))) {
}
/* Known Answer Suppresion */
- for (n = flx_dns_packet_get_field(p, DNS_FIELD_ANCOUNT); n > 0; n --) {
+ for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT); n > 0; n --) {
flxRecord *record;
gboolean unique = FALSE;
g_assert(i);
g_assert(a);
- for (n = flx_dns_packet_get_field(p, DNS_FIELD_ANCOUNT) +
- flx_dns_packet_get_field(p, DNS_FIELD_ARCOUNT); n > 0; n--) {
+ 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 (flx_dns_packet_is_query(p)) {
- if (flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT) == 0 ||
- flx_dns_packet_get_field(p, DNS_FIELD_ARCOUNT) != 0 ||
- flx_dns_packet_get_field(p, DNS_FIELD_NSCOUNT) != 0) {
+ if (flx_dns_packet_get_field(p, FLX_DNS_FIELD_QDCOUNT) == 0 ||
+ flx_dns_packet_get_field(p, FLX_DNS_FIELD_ARCOUNT) != 0) {
g_warning("Invalid query packet.");
return;
}
handle_query(s, p, i, &a);
g_message("Handled query");
} else {
- if (flx_dns_packet_get_field(p, DNS_FIELD_QDCOUNT) != 0 ||
- flx_dns_packet_get_field(p, DNS_FIELD_ANCOUNT) == 0 ||
- flx_dns_packet_get_field(p, DNS_FIELD_NSCOUNT) != 0) {
+ if (flx_dns_packet_get_field(p, FLX_DNS_FIELD_QDCOUNT) != 0 ||
+ flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT) == 0 ||
+ flx_dns_packet_get_field(p, FLX_DNS_FIELD_NSCOUNT) != 0) {
g_warning("Invalid response packet.");
return;
}
uname(&utsname);
r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
- flx_server_add(s, 0, 0, AF_UNSPEC, TRUE, r);
+ flx_server_add(s, 0, 0, AF_UNSPEC, FLX_SERVER_ENTRY_UNIQUE, r);
flx_record_unref(r);
/* Add localhost entries */
flx_address_parse("127.0.0.1", AF_INET, &a);
- flx_server_add_address(s, 0, 0, AF_UNSPEC, TRUE, "localhost", &a);
+ flx_server_add_address(s, 0, 0, AF_UNSPEC, FLX_SERVER_ENTRY_UNIQUE|FLX_SERVER_ENTRY_NOPROBE|FLX_SERVER_ENTRY_NOANNOUNCE, "localhost", &a);
flx_address_parse("::1", AF_INET6, &a);
- flx_server_add_address(s, 0, 0, AF_UNSPEC, TRUE, "ip6-localhost", &a);
+ flx_server_add_address(s, 0, 0, AF_UNSPEC, FLX_SERVER_ENTRY_UNIQUE|FLX_SERVER_ENTRY_NOPROBE|FLX_SERVER_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
}
flxServer *flx_server_new(GMainContext *c) {
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
flxRecord *r) {
flxServerEntry *e, *t;
e->id = id;
e->interface = interface;
e->protocol = protocol;
- e->unique = unique;
+ e->flags = flags;
FLX_LLIST_HEAD_INIT(flxAnnouncement, e->announcements);
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
const gchar *name,
const gchar *dest) {
r = flx_record_new_full(name ? name : s->hostname, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_PTR);
r->data.ptr.name = flx_normalize_name(dest);
- flx_server_add(s, id, interface, protocol, unique, r);
+ flx_server_add(s, id, interface, protocol, flags, r);
flx_record_unref(r);
}
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
const gchar *name,
flxAddress *a) {
r = flx_record_new_full(name, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_A);
r->data.a.address = a->data.ipv4;
- flx_server_add(s, id, interface, protocol, unique, r);
+ flx_server_add(s, id, interface, protocol, flags, r);
flx_record_unref(r);
reverse = flx_reverse_lookup_name_ipv4(&a->data.ipv4);
g_assert(reverse);
- flx_server_add_ptr(s, id, interface, protocol, unique, reverse, name);
+ flx_server_add_ptr(s, id, interface, protocol, flags, reverse, name);
g_free(reverse);
} else {
r = flx_record_new_full(name, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_AAAA);
r->data.aaaa.address = a->data.ipv6;
- flx_server_add(s, id, interface, protocol, unique, r);
+ flx_server_add(s, id, interface, protocol, flags, r);
flx_record_unref(r);
reverse = flx_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
g_assert(reverse);
- flx_server_add_ptr(s, id, interface, protocol, unique, reverse, name);
+ flx_server_add_ptr(s, id, interface, protocol, flags, reverse, name);
g_free(reverse);
reverse = flx_reverse_lookup_name_ipv6_int(&a->data.ipv6);
g_assert(reverse);
- flx_server_add_ptr(s, id, interface, protocol, unique, reverse, name);
+ flx_server_add_ptr(s, id, interface, protocol, flags, reverse, name);
g_free(reverse);
}
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
const gchar *name,
va_list va) {
r = flx_record_new_full(name ? name : s->hostname, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_TXT);
r->data.txt.string_list = flx_string_list_new_va(va);
- flx_server_add(s, id, interface, protocol, unique, r);
+ flx_server_add(s, id, interface, protocol, flags, r);
flx_record_unref(r);
}
gint id,
gint interface,
guchar protocol,
- gboolean unique,
+ flxServerEntryFlags flags,
const gchar *name,
...) {
g_assert(s);
va_start(va, name);
- flx_server_add_text_va(s, id, interface, protocol, unique, name, va);
+ flx_server_add_text_va(s, id, interface, protocol, flags, name, va);
va_end(va);
}
flx_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
}
+struct tmpdata {
+ flxRecord *record;
+ gboolean flush_cache;
+};
+
static void post_response_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) {
- flxRecord *r = userdata;
+ struct tmpdata *tmpdata = userdata;
g_assert(m);
g_assert(i);
- g_assert(r);
+ g_assert(tmpdata);
- flx_interface_post_response(i, NULL, r, FALSE);
+ flx_interface_post_response(i, NULL, tmpdata->record, tmpdata->flush_cache, FALSE);
}
-void flx_server_post_response(flxServer *s, gint interface, guchar protocol, flxRecord *record) {
+void flx_server_post_response(flxServer *s, gint interface, guchar protocol, flxRecord *record, gboolean flush_cache) {
+ struct tmpdata tmpdata;
+
g_assert(s);
g_assert(record);
- flx_interface_monitor_walk(s->monitor, interface, protocol, post_response_callback, record);
+ tmpdata.record = record;
+ tmpdata.flush_cache = flush_cache;
+
+ flx_interface_monitor_walk(s->monitor, interface, protocol, post_response_callback, &tmpdata);
}
gint interface;
guchar protocol;
- gboolean unique;
+ flxServerEntryFlags flags;
FLX_LLIST_FIELDS(flxServerEntry, entry);
FLX_LLIST_FIELDS(flxServerEntry, by_key);