1 #include <sys/socket.h>
4 #include <sys/utsname.h>
10 static gint timeval_cmp(const GTimeVal *a, const GTimeVal *b) {
14 if (a->tv_sec < b->tv_sec)
17 if (a->tv_sec > b->tv_sec)
20 if (a->tv_usec < b->tv_usec)
23 if (a->tv_usec > b->tv_usec)
29 static gint query_job_instance_compare(gpointer a, gpointer b) {
30 flxQueryJobInstance *j = a, *k = b;
34 return timeval_cmp(&j->job->time, &k->job->time);
37 static gint response_job_instance_compare(gpointer a, gpointer b) {
38 flxResponseJobInstance *j = a, *k = b;
42 return timeval_cmp(&j->job->time, &k->job->time);
45 flxServer *flx_server_new(GMainContext *c) {
46 gchar *hn, *e, *hinfo;
47 struct utsname utsname;
50 flxServer *s = g_new(flxServer, 1);
53 g_main_context_ref(c);
56 s->context = g_main_context_default();
59 s->rrset_by_id = g_hash_table_new(g_int_hash, g_int_equal);
60 s->rrset_by_name = g_hash_table_new(g_str_hash, g_str_equal);
63 s->query_job_queue = flx_prio_queue_new(query_job_instance_compare);
64 s->response_job_queue = flx_prio_queue_new(response_job_instance_compare);
66 s->monitor = flx_interface_monitor_new(s);
69 hn = flx_get_host_name();
70 if ((e = strchr(hn, '.')))
73 s->hostname = g_strdup_printf("%s.local.", hn);
76 /* Fill in HINFO rr */
77 s->hinfo_rr_id = flx_server_get_next_id(s);
80 hinfo = g_strdup_printf("%s%c%s%n", g_strup(utsname.machine), 0, g_strup(utsname.sysname), &length);
82 flx_server_add(s, s->hinfo_rr_id, 0, AF_UNSPEC,
83 s->hostname, FLX_DNS_TYPE_HINFO, hinfo, length+1);
86 /* Add localhost entries */
87 flx_address_parse("127.0.0.1", AF_INET, &a);
88 flx_server_add_address(s, 0, 0, AF_UNSPEC, "localhost", &a);
90 flx_address_parse("::1", AF_INET6, &a);
91 flx_server_add_address(s, 0, 0, AF_UNSPEC, "ip6-localhost", &a);
96 void flx_server_free(flxServer* s) {
99 while (s->query_job_queue->last)
100 flx_server_remove_query_job_instance(s, s->query_job_queue->last->data);
102 flx_prio_queue_free(s->query_job_queue);
103 flx_prio_queue_free(s->response_job_queue);
105 flx_interface_monitor_free(s->monitor);
107 flx_server_remove(s, 0);
109 g_hash_table_destroy(s->rrset_by_id);
110 g_hash_table_destroy(s->rrset_by_name);
111 g_main_context_unref(s->context);
117 gint flx_server_get_next_id(flxServer *s) {
120 return s->current_id++;
123 void flx_server_add_rr(flxServer *s, gint id, gint interface, guchar protocol, const flxRecord *rr) {
131 e = g_new(flxEntry, 1);
132 flx_record_copy_normalize(&e->rr, rr);
134 e->interface = interface;
135 e->protocol = protocol;
137 /* Insert into linked list */
139 if ((e->next = s->entries))
143 /* Insert into hash table indexed by id */
144 e->prev_by_id = NULL;
145 if ((e->next_by_id = g_hash_table_lookup(s->rrset_by_id, &id)))
146 e->next_by_id->prev = e;
147 g_hash_table_replace(s->rrset_by_id, &e->id, e);
149 /* Insert into hash table indexed by name */
150 e->prev_by_name = NULL;
151 if ((e->next_by_name = g_hash_table_lookup(s->rrset_by_name, e->rr.name)))
152 e->next_by_name->prev = e;
153 g_hash_table_replace(s->rrset_by_name, e->rr.name, e);
156 void flx_server_add(flxServer *s, gint id, gint interface, guchar protocol, const gchar *name, guint16 type, gconstpointer data, guint size) {
163 rr.name = (gchar*) name;
165 rr.class = FLX_DNS_CLASS_IN;
166 rr.data = (gpointer) data;
168 rr.ttl = FLX_DEFAULT_TTL;
169 flx_server_add_rr(s, id, interface, protocol, &rr);
172 const flxRecord *flx_server_iterate(flxServer *s, gint id, void **state) {
173 flxEntry **e = (flxEntry**) state;
178 *e = id > 0 ? (*e)->next_by_id : (*e)->next;
180 *e = id > 0 ? g_hash_table_lookup(s->rrset_by_id, &id) : s->entries;
188 static void free_entry(flxServer*s, flxEntry *e) {
191 /* Remove from linked list */
193 e->prev->next = e->next;
195 s->entries = e->next;
198 e->next->prev = e->prev;
200 /* Remove from hash table indexed by id */
202 e->prev_by_id = e->next_by_id;
205 g_hash_table_replace(s->rrset_by_id, &e->next_by_id->id, e->next_by_id);
207 g_hash_table_remove(s->rrset_by_id, &e->id);
211 e->next_by_id->prev_by_id = e->prev_by_id;
213 /* Remove from hash table indexed by name */
215 e->prev_by_name = e->next_by_name;
218 g_hash_table_replace(s->rrset_by_name, &e->next_by_name->rr.name, e->next_by_name);
220 g_hash_table_remove(s->rrset_by_name, &e->rr.name);
224 e->next_by_name->prev_by_name = e->prev_by_name;
227 void flx_server_remove(flxServer *s, gint id) {
232 free_entry(s, s->entries);
236 while ((e = g_hash_table_lookup(s->rrset_by_id, &id)))
241 flxRecord *flx_record_copy_normalize(flxRecord *ret_dest, const flxRecord*src) {
246 ret_dest->name = flx_normalize_name(src->name);
247 ret_dest->data = g_memdup(src->data, src->size);
252 static const gchar *dns_class_to_string(guint16 class) {
253 if (class == FLX_DNS_CLASS_IN)
259 static const gchar *dns_type_to_string(guint16 type) {
263 case FLX_DNS_TYPE_AAAA:
265 case FLX_DNS_TYPE_PTR:
267 case FLX_DNS_TYPE_HINFO:
269 case FLX_DNS_TYPE_TXT:
276 void flx_server_dump(flxServer *s, FILE *f) {
281 for (e = s->entries; e; e = e->next) {
283 fprintf(f, "%i.%u: %-40s %-8s %-8s ", e->interface, e->protocol, e->rr.name, dns_class_to_string(e->rr.class), dns_type_to_string(e->rr.type));
287 if (e->rr.class == FLX_DNS_CLASS_IN) {
288 if (e->rr.type == FLX_DNS_TYPE_A)
289 inet_ntop(AF_INET, e->rr.data, t, sizeof(t));
290 else if (e->rr.type == FLX_DNS_TYPE_AAAA)
291 inet_ntop(AF_INET6, e->rr.data, t, sizeof(t));
292 else if (e->rr.type == FLX_DNS_TYPE_PTR)
293 g_strlcpy(t, e->rr.data, sizeof(t));
294 else if (e->rr.type == FLX_DNS_TYPE_HINFO) {
297 if ((s2 = memchr(e->rr.data, 0, e->rr.size))) {
299 if (memchr(s2, 0, e->rr.size - ((char*) s2 - (char*) e->rr.data)))
300 snprintf(t, sizeof(t), "'%s' '%s'", (char*) e->rr.data, s2);
303 } else if (e->rr.type == FLX_DNS_TYPE_TXT) {
310 memcpy(t, e->rr.data, l);
315 fprintf(f, "%s\n", t);
319 void flx_server_add_address(flxServer *s, gint id, gint interface, guchar protocol, const gchar *name, flxAddress *a) {
324 n = flx_normalize_name(name ? name : s->hostname);
326 if (a->family == AF_INET) {
329 flx_server_add(s, id, interface, protocol, n, FLX_DNS_TYPE_A, &a->ipv4, sizeof(a->ipv4));
331 r = flx_reverse_lookup_name_ipv4(&a->ipv4);
333 flx_server_add(s, id, interface, protocol, r, FLX_DNS_TYPE_PTR, n, strlen(n)+1);
339 flx_server_add(s, id, interface, protocol, n, FLX_DNS_TYPE_AAAA, &a->ipv6, sizeof(a->ipv6));
341 r = flx_reverse_lookup_name_ipv6_arpa(&a->ipv6);
343 flx_server_add(s, id, interface, protocol, r, FLX_DNS_TYPE_PTR, n, strlen(n)+1);
346 r = flx_reverse_lookup_name_ipv6_int(&a->ipv6);
348 flx_server_add(s, id, interface, protocol, r, FLX_DNS_TYPE_PTR, n, strlen(n)+1);
355 void flx_server_add_text(flxServer *s, gint id, gint interface, guchar protocol, const gchar *name, const gchar *text) {
360 n = flx_normalize_name(name ? name : s->hostname);
361 flx_server_add(s, id, interface, protocol, n, FLX_DNS_TYPE_TXT, text, strlen(text));
366 flxQueryJob* flx_query_job_new(void) {
367 flxQueryJob *job = g_new(flxQueryJob, 1);
368 job->query.name = NULL;
369 job->query.class = 0;
372 job->time.tv_sec = 0;
373 job->time.tv_usec = 0;
377 flxQueryJob* flx_query_job_ref(flxQueryJob *job) {
379 g_assert(job->ref >= 1);
384 void flx_query_job_unref(flxQueryJob *job) {
386 g_assert(job->ref >= 1);
391 static gboolean query_job_exists(flxServer *s, gint interface, guchar protocol, flxQuery *q) {
396 for (n = s->query_job_queue->root; n; n = n->next)
397 if (flx_query_equal(&((flxQueryJobInstance*) n->data)->job->query, q))
403 static void post_query_job(flxServer *s, gint interface, guchar protocol, flxQueryJob *job) {
407 if (interface <= 0) {
408 const flxInterface *i;
410 for (i = flx_interface_monitor_get_first(s->monitor); i; i = i->next)
411 post_query_job(s, i->index, protocol, job);
412 } else if (protocol == AF_UNSPEC) {
413 post_query_job(s, interface, AF_INET, job);
414 post_query_job(s, interface, AF_INET6, job);
416 flxQueryJobInstance *i;
418 if (query_job_exists(s, interface, protocol, &job->query))
421 i = g_new(flxQueryJobInstance, 1);
422 i->job = flx_query_job_ref(job);
423 i->interface = interface;
424 i->protocol = protocol;
425 i->node = flx_prio_queue_put(s->query_job_queue, i);
429 void flx_server_post_query_job(flxServer *s, gint interface, guchar protocol, const GTimeVal *tv, const flxQuery *q) {
434 job = flx_query_job_new();
435 job->query.name = g_strdup(q->name);
436 job->query.class = q->class;
437 job->query.type = q->type;
440 post_query_job(s, interface, protocol, job);
443 void flx_server_drop_query_job(flxServer *s, gint interface, guchar protocol, const flxQuery *q) {
444 flxPrioQueueNode *n, *next;
446 g_assert(interface > 0);
447 g_assert(protocol != AF_UNSPEC);
450 for (n = s->query_job_queue->root; n; n = next) {
453 if (flx_query_equal(&((flxQueryJobInstance*) n->data)->job->query, q))
454 flx_server_remove_query_job_instance(s, n->data);
458 void flx_server_remove_query_job_instance(flxServer *s, flxQueryJobInstance *i) {
463 flx_prio_queue_remove(s->query_job_queue, i->node);
464 flx_query_job_unref(i->job);
468 gboolean flx_query_equal(const flxQuery *a, const flxQuery *b) {
469 return strcmp(a->name, b->name) == 0 && a->type == b->type && a->class == b->class;