]> git.meshlink.io Git - catta/blob - server.c
5cdf56d9cc97de18020c927322b35aef30421e33
[catta] / server.c
1 #include <sys/socket.h>
2 #include <arpa/inet.h>
3 #include <string.h>
4 #include <sys/utsname.h>
5 #include <unistd.h>
6
7 #include "server.h"
8 #include "util.h"
9 #include "iface.h"
10 #include "socket.h"
11 #include "subscribe.h"
12
13 static void free_entry(flxServer*s, flxEntry *e) {
14     flxEntry *t;
15
16     g_assert(s);
17     g_assert(e);
18
19     flx_goodbye_entry(s, e, TRUE);
20
21     /* Remove from linked list */
22     FLX_LLIST_REMOVE(flxEntry, entries, s->entries, e);
23
24     /* Remove from hash table indexed by name */
25     t = g_hash_table_lookup(s->entries_by_key, e->record->key);
26     FLX_LLIST_REMOVE(flxEntry, by_key, t, e);
27     if (t)
28         g_hash_table_replace(s->entries_by_key, t->record->key, t);
29     else
30         g_hash_table_remove(s->entries_by_key, e->record->key);
31
32     /* Remove from associated group */
33     if (e->group)
34         FLX_LLIST_REMOVE(flxEntry, by_group, e->group->entries, e);
35
36     flx_record_unref(e->record);
37     g_free(e);
38 }
39
40 static void free_group(flxServer *s, flxEntryGroup *g) {
41     g_assert(s);
42     g_assert(g);
43
44     while (g->entries)
45         free_entry(s, g->entries);
46
47     FLX_LLIST_REMOVE(flxEntryGroup, groups, s->groups, g);
48     g_free(g);
49 }
50
51 static void cleanup_dead(flxServer *s) {
52     flxEntryGroup *g, *ng;
53     flxEntry *e, *ne;
54     g_assert(s);
55
56
57     if (s->need_group_cleanup) {
58         for (g = s->groups; g; g = ng) {
59             ng = g->groups_next;
60             
61             if (g->dead)
62                 free_group(s, g);
63         }
64
65         s->need_group_cleanup = FALSE;
66     }
67
68     if (s->need_entry_cleanup) {
69         for (e = s->entries; e; e = ne) {
70             ne = e->entries_next;
71             
72             if (e->dead)
73                 free_entry(s, e);
74         }
75
76         s->need_entry_cleanup = FALSE;
77     }
78 }
79
80 static void handle_query_key(flxServer *s, flxKey *k, flxInterface *i, const flxAddress *a) {
81     flxEntry *e;
82     gchar *txt;
83     
84     g_assert(s);
85     g_assert(k);
86     g_assert(i);
87     g_assert(a);
88
89     g_message("Handling query: %s", txt = flx_key_to_string(k));
90     g_free(txt);
91
92     flx_packet_scheduler_incoming_query(i->scheduler, k);
93
94     if (k->type == FLX_DNS_TYPE_ANY) {
95
96         /* Handle ANY query */
97         
98         for (e = s->entries; e; e = e->entries_next)
99             if (!e->dead && flx_key_pattern_match(k, e->record->key) && flx_entry_registered(s, e, i))
100                 flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, FALSE);
101     } else {
102
103         /* Handle all other queries */
104         
105         for (e = g_hash_table_lookup(s->entries_by_key, k); e; e = e->by_key_next)
106             if (!e->dead && flx_entry_registered(s, e, i))
107                 flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, FALSE);
108     }
109 }
110
111 static void withdraw_entry(flxServer *s, flxEntry *e) {
112     g_assert(s);
113     g_assert(e);
114
115     
116     if (e->group) {
117         flxEntry *k;
118         
119         for (k = e->group->entries; k; k = k->by_group_next) {
120             flx_goodbye_entry(s, k, FALSE);
121             k->dead = TRUE;
122         }
123         
124         flx_entry_group_change_state(e->group, FLX_ENTRY_GROUP_COLLISION);
125     } else {
126         flx_goodbye_entry(s, e, FALSE);
127         e->dead = TRUE;
128     }
129
130     s->need_entry_cleanup = TRUE;
131 }
132
133 static void incoming_probe(flxServer *s, flxRecord *record, flxInterface *i) {
134     flxEntry *e, *n;
135     gchar *t;
136     
137     g_assert(s);
138     g_assert(record);
139     g_assert(i);
140
141     t = flx_record_to_string(record);
142
143 /*     g_message("PROBE: [%s]", t); */
144
145     
146     for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
147         n = e->by_key_next;
148
149         if (e->dead || flx_record_equal_no_ttl(record, e->record))
150             continue;
151
152         if (flx_entry_registering(s, e, i)) {
153             gint cmp;
154
155             if ((cmp = flx_record_lexicographical_compare(record, e->record)) > 0) {
156                 withdraw_entry(s, e);
157                 g_message("Recieved conflicting probe [%s]. Local host lost. Withdrawing.", t);
158             } else if (cmp < 0)
159                 g_message("Recieved conflicting probe [%s]. Local host won.", t);
160
161         }
162     }
163
164     g_free(t);
165 }
166
167 static void handle_query(flxServer *s, flxDnsPacket *p, flxInterface *i, const flxAddress *a) {
168     guint n;
169     
170     g_assert(s);
171     g_assert(p);
172     g_assert(i);
173     g_assert(a);
174
175     /* Handle the questions */
176     for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_QDCOUNT); n > 0; n --) {
177         flxKey *key;
178
179         if (!(key = flx_dns_packet_consume_key(p))) {
180             g_warning("Packet too short (1)");
181             return;
182         }
183
184         handle_query_key(s, key, i, a);
185         flx_key_unref(key);
186     }
187
188     /* Known Answer Suppresion */
189     for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT); n > 0; n --) {
190         flxRecord *record;
191         gboolean unique = FALSE;
192
193         if (!(record = flx_dns_packet_consume_record(p, &unique))) {
194             g_warning("Packet too short (2)");
195             return;
196         }
197
198         flx_packet_scheduler_incoming_known_answer(i->scheduler, record, a);
199         flx_record_unref(record);
200     }
201
202     /* Probe record */
203     for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_NSCOUNT); n > 0; n --) {
204         flxRecord *record;
205         gboolean unique = FALSE;
206
207         if (!(record = flx_dns_packet_consume_record(p, &unique))) {
208             g_warning("Packet too short (3)");
209             return;
210         }
211
212         if (record->key->type != FLX_DNS_TYPE_ANY)
213             incoming_probe(s, record, i);
214         
215         flx_record_unref(record);
216     }
217 }
218
219 static gboolean handle_conflict(flxServer *s, flxInterface *i, flxRecord *record, gboolean unique, const flxAddress *a) {
220     gboolean valid = TRUE;
221     flxEntry *e, *n;
222     gchar *t;
223     
224     g_assert(s);
225     g_assert(i);
226     g_assert(record);
227
228     t = flx_record_to_string(record);
229
230 /*     g_message("CHECKING FOR CONFLICT: [%s]", t); */
231
232     for (e = g_hash_table_lookup(s->entries_by_key, record->key); e; e = n) {
233         n = e->by_key_next;
234
235         if (e->dead)
236             continue;
237         
238         if (flx_entry_registered(s, e, i)) {
239
240             gboolean equal = flx_record_equal_no_ttl(record, e->record);
241                 
242             /* Check whether there is a unique record conflict */
243             if (!equal && ((e->flags & FLX_ENTRY_UNIQUE) || unique)) {
244                 gint cmp;
245                 
246                 /* The lexicographically later data wins. */
247                 if ((cmp = flx_record_lexicographical_compare(record, e->record)) > 0) {
248                     g_message("Recieved conflicting record [%s]. Local host lost. Withdrawing.", t);
249                     withdraw_entry(s, e);
250                 } else if (cmp < 0) {
251                     /* Tell the other host that our entry is lexicographically later */
252
253                     g_message("Recieved conflicting record [%s]. Local host won. Refreshing.", t);
254
255                     valid = FALSE;
256                     flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, TRUE);
257                 }
258                 
259                 /* Check wheter there is a TTL conflict */
260             } else if (equal && record->ttl <= e->record->ttl/2) {
261                 /* Correct the TTL */
262                 valid = FALSE;
263                 flx_interface_post_response(i, a, e->record, e->flags & FLX_ENTRY_UNIQUE, TRUE);
264                 g_message("Recieved record with bad TTL [%s]. Refreshing.", t);
265             }
266             
267         } else if (flx_entry_registering(s, e, i)) {
268
269             if (!flx_record_equal_no_ttl(record, e->record) && ((e->flags & FLX_ENTRY_UNIQUE) || unique)) {
270
271                 /* We are currently registering a matching record, but
272                  * someone else already claimed it, so let's
273                  * withdraw */
274                 
275                 g_message("Recieved conflicting record [%s] with local record to be. Withdrawing.", t);
276                 withdraw_entry(s, e);
277             }
278         }
279     }
280
281     g_free(t);
282
283     return valid;
284 }
285
286 static void handle_response(flxServer *s, flxDnsPacket *p, flxInterface *i, const flxAddress *a) {
287     guint n;
288     
289     g_assert(s);
290     g_assert(p);
291     g_assert(i);
292     g_assert(a);
293     
294     for (n = flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT) +
295              flx_dns_packet_get_field(p, FLX_DNS_FIELD_ARCOUNT); n > 0; n--) {
296         flxRecord *record;
297         gboolean cache_flush = FALSE;
298         gchar *txt;
299         
300         if (!(record = flx_dns_packet_consume_record(p, &cache_flush))) {
301             g_warning("Packet too short (4)");
302             return;
303         }
304
305         if (record->key->type != FLX_DNS_TYPE_ANY) {
306
307             g_message("Handling response: %s", txt = flx_record_to_string(record));
308             g_free(txt);
309             
310             if (handle_conflict(s, i, record, cache_flush, a)) {
311                 flx_cache_update(i->cache, record, cache_flush, a);
312                 flx_packet_scheduler_incoming_response(i->scheduler, record);
313             }
314         }
315             
316         flx_record_unref(record);
317     }
318 }
319
320 static void dispatch_packet(flxServer *s, flxDnsPacket *p, struct sockaddr *sa, gint iface, gint ttl) {
321     flxInterface *i;
322     flxAddress a;
323     
324     g_assert(s);
325     g_assert(p);
326     g_assert(sa);
327     g_assert(iface > 0);
328
329     g_message("new packet recieved.");
330
331     if (!(i = flx_interface_monitor_get_interface(s->monitor, iface, sa->sa_family))) {
332         g_warning("Recieved packet from invalid interface.");
333         return;
334     }
335
336     if (ttl != 255) {
337         g_warning("Recieved packet with invalid TTL on interface '%s.%i'.", i->hardware->name, i->protocol);
338         if (!s->ignore_bad_ttl)
339             return;
340     }
341
342     if (sa->sa_family == AF_INET6) {
343         static const unsigned char ipv4_in_ipv6[] = {
344             0x00, 0x00, 0x00, 0x00,
345             0x00, 0x00, 0x00, 0x00,
346             0xFF, 0xFF, 0xFF, 0xFF };
347
348         if (memcmp(((struct sockaddr_in6*) sa)->sin6_addr.s6_addr, ipv4_in_ipv6, sizeof(ipv4_in_ipv6)) == 0) {
349
350             /* This is an IPv4 address encapsulated in IPv6, so let's ignore it. */
351             return;
352         }
353     }
354
355     if (flx_dns_packet_check_valid(p) < 0) {
356         g_warning("Recieved invalid packet.");
357         return;
358     }
359
360     flx_address_from_sockaddr(sa, &a);
361
362     if (flx_dns_packet_is_query(p)) {
363
364         if (flx_dns_packet_get_field(p, FLX_DNS_FIELD_QDCOUNT) == 0 ||
365             flx_dns_packet_get_field(p, FLX_DNS_FIELD_ARCOUNT) != 0) {
366             g_warning("Invalid query packet.");
367             return;
368         }
369                 
370         handle_query(s, p, i, &a);    
371         g_message("Handled query");
372     } else {
373         if (flx_dns_packet_get_field(p, FLX_DNS_FIELD_QDCOUNT) != 0 ||
374             flx_dns_packet_get_field(p, FLX_DNS_FIELD_ANCOUNT) == 0 ||
375             flx_dns_packet_get_field(p, FLX_DNS_FIELD_NSCOUNT) != 0) {
376             g_warning("Invalid response packet.");
377             return;
378         }
379
380         handle_response(s, p, i, &a);
381         g_message("Handled response");
382     }
383 }
384
385 static void work(flxServer *s) {
386     struct sockaddr_in6 sa6;
387     struct sockaddr_in sa;
388     flxDnsPacket *p;
389     gint iface = -1;
390     guint8 ttl;
391         
392     g_assert(s);
393
394     if (s->pollfd_ipv4.revents & G_IO_IN) {
395         if ((p = flx_recv_dns_packet_ipv4(s->fd_ipv4, &sa, &iface, &ttl))) {
396             dispatch_packet(s, p, (struct sockaddr*) &sa, iface, ttl);
397             flx_dns_packet_free(p);
398         }
399     }
400
401     if (s->pollfd_ipv6.revents & G_IO_IN) {
402         if ((p = flx_recv_dns_packet_ipv6(s->fd_ipv6, &sa6, &iface, &ttl))) {
403             dispatch_packet(s, p, (struct sockaddr*) &sa6, iface, ttl);
404             flx_dns_packet_free(p);
405         }
406     }
407 }
408
409 static gboolean prepare_func(GSource *source, gint *timeout) {
410     g_assert(source);
411     g_assert(timeout);
412     
413     *timeout = -1;
414     return FALSE;
415 }
416
417 static gboolean check_func(GSource *source) {
418     flxServer* s;
419     g_assert(source);
420
421     s = *((flxServer**) (((guint8*) source) + sizeof(GSource)));
422     g_assert(s);
423     
424     return (s->pollfd_ipv4.revents | s->pollfd_ipv6.revents) & (G_IO_IN | G_IO_HUP | G_IO_ERR);
425 }
426
427 static gboolean dispatch_func(GSource *source, GSourceFunc callback, gpointer user_data) {
428     flxServer* s;
429     g_assert(source);
430
431     s = *((flxServer**) (((guint8*) source) + sizeof(GSource)));
432     g_assert(s);
433
434     work(s);
435     cleanup_dead(s);
436
437     return TRUE;
438 }
439
440 static void add_default_entries(flxServer *s) {
441     gint length = 0;
442     struct utsname utsname;
443     gchar *hinfo;
444     flxAddress a;
445     flxRecord *r;
446     
447     g_assert(s);
448     
449     /* Fill in HINFO rr */
450     r = flx_record_new_full(s->hostname, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_HINFO);
451     uname(&utsname);
452     r->data.hinfo.cpu = g_strdup(g_strup(utsname.machine));
453     r->data.hinfo.os = g_strdup(g_strup(utsname.sysname));
454     flx_server_add(s, NULL, 0, AF_UNSPEC, FLX_ENTRY_UNIQUE, r);
455     flx_record_unref(r);
456
457     /* Add localhost entries */
458     flx_address_parse("127.0.0.1", AF_INET, &a);
459     flx_server_add_address(s, NULL, 0, AF_UNSPEC, FLX_ENTRY_UNIQUE|FLX_ENTRY_NOPROBE|FLX_ENTRY_NOANNOUNCE, "localhost", &a);
460
461     flx_address_parse("::1", AF_INET6, &a);
462     flx_server_add_address(s, NULL, 0, AF_UNSPEC, FLX_ENTRY_UNIQUE|FLX_ENTRY_NOPROBE|FLX_ENTRY_NOANNOUNCE, "ip6-localhost", &a);
463 }
464
465 flxServer *flx_server_new(GMainContext *c) {
466     gchar *hn, *e;
467     flxServer *s;
468     
469     static GSourceFuncs source_funcs = {
470         prepare_func,
471         check_func,
472         dispatch_func,
473         NULL,
474         NULL,
475         NULL
476     };
477
478     s = g_new(flxServer, 1);
479
480     s->ignore_bad_ttl = FALSE;
481     s->need_entry_cleanup = s->need_group_cleanup = FALSE;
482     
483     s->fd_ipv4 = flx_open_socket_ipv4();
484     s->fd_ipv6 = flx_open_socket_ipv6();
485     
486     if (s->fd_ipv6 < 0 && s->fd_ipv4 < 0) {
487         g_critical("Failed to create IP sockets.\n");
488         g_free(s);
489         return NULL;
490     }
491
492     if (s->fd_ipv4 < 0)
493         g_message("Failed to create IPv4 socket, proceeding in IPv6 only mode");
494     else if (s->fd_ipv6 < 0)
495         g_message("Failed to create IPv6 socket, proceeding in IPv4 only mode");
496     
497     if (c)
498         g_main_context_ref(s->context = c);
499     else
500         s->context = g_main_context_default();
501     
502     FLX_LLIST_HEAD_INIT(flxEntry, s->entries);
503     s->entries_by_key = g_hash_table_new((GHashFunc) flx_key_hash, (GEqualFunc) flx_key_equal);
504     FLX_LLIST_HEAD_INIT(flxGroup, s->groups);
505
506     FLX_LLIST_HEAD_INIT(flxSubscription, s->subscriptions);
507     s->subscription_hashtable = g_hash_table_new((GHashFunc) flx_key_hash, (GEqualFunc) flx_key_equal);
508
509     /* Get host name */
510     hn = flx_get_host_name();
511     hn[strcspn(hn, ".")] = 0;
512
513     s->hostname = g_strdup_printf("%s.local.", hn);
514     g_free(hn);
515
516     s->time_event_queue = flx_time_event_queue_new(s->context, G_PRIORITY_DEFAULT+10); /* Slightly less priority than the FDs */
517     s->monitor = flx_interface_monitor_new(s);
518     flx_interface_monitor_sync(s->monitor);
519     add_default_entries(s);
520     
521     /* Prepare IO source registration */
522     s->source = g_source_new(&source_funcs, sizeof(GSource) + sizeof(flxServer*));
523     *((flxServer**) (((guint8*) s->source) + sizeof(GSource))) = s;
524
525     memset(&s->pollfd_ipv4, 0, sizeof(s->pollfd_ipv4));
526     s->pollfd_ipv4.fd = s->fd_ipv4;
527     s->pollfd_ipv4.events = G_IO_IN|G_IO_ERR|G_IO_HUP;
528     g_source_add_poll(s->source, &s->pollfd_ipv4);
529     
530     memset(&s->pollfd_ipv6, 0, sizeof(s->pollfd_ipv6));
531     s->pollfd_ipv6.fd = s->fd_ipv6;
532     s->pollfd_ipv6.events = G_IO_IN|G_IO_ERR|G_IO_HUP;
533     g_source_add_poll(s->source, &s->pollfd_ipv6);
534
535     g_source_attach(s->source, s->context);
536
537     return s;
538 }
539
540 void flx_server_free(flxServer* s) {
541     g_assert(s);
542
543     while(s->entries)
544         free_entry(s, s->entries);
545
546     flx_interface_monitor_free(s->monitor);
547
548     while (s->groups)
549         free_group(s, s->groups);
550
551     while (s->subscriptions)
552         flx_subscription_free(s->subscriptions);
553     g_hash_table_destroy(s->subscription_hashtable);
554
555     g_hash_table_destroy(s->entries_by_key);
556
557     flx_time_event_queue_free(s->time_event_queue);
558
559     if (s->fd_ipv4 >= 0)
560         close(s->fd_ipv4);
561     if (s->fd_ipv6 >= 0)
562         close(s->fd_ipv6);
563     
564     g_free(s->hostname);
565
566     g_source_destroy(s->source);
567     g_source_unref(s->source);
568     g_main_context_unref(s->context);
569
570     g_free(s);
571 }
572
573 void flx_server_add(
574     flxServer *s,
575     flxEntryGroup *g,
576     gint interface,
577     guchar protocol,
578     flxEntryFlags flags,
579     flxRecord *r) {
580     
581     flxEntry *e, *t;
582     g_assert(s);
583     g_assert(r);
584
585     g_assert(r->key->type != FLX_DNS_TYPE_ANY);
586
587     e = g_new(flxEntry, 1);
588     e->server = s;
589     e->record = flx_record_ref(r);
590     e->group = g;
591     e->interface = interface;
592     e->protocol = protocol;
593     e->flags = flags;
594     e->dead = FALSE;
595
596     FLX_LLIST_HEAD_INIT(flxAnnouncement, e->announcements);
597
598     FLX_LLIST_PREPEND(flxEntry, entries, s->entries, e);
599
600     /* Insert into hash table indexed by name */
601     t = g_hash_table_lookup(s->entries_by_key, e->record->key);
602     FLX_LLIST_PREPEND(flxEntry, by_key, t, e);
603     g_hash_table_replace(s->entries_by_key, e->record->key, t);
604
605     /* Insert into group list */
606     if (g)
607         FLX_LLIST_PREPEND(flxEntry, by_group, g->entries, e); 
608
609     flx_announce_entry(s, e);
610 }
611 const flxRecord *flx_server_iterate(flxServer *s, flxEntryGroup *g, void **state) {
612     flxEntry **e = (flxEntry**) state;
613     g_assert(s);
614     g_assert(e);
615
616     if (!*e)
617         *e = g ? g->entries : s->entries;
618     
619     while (*e && (*e)->dead)
620         *e = g ? (*e)->by_group_next : (*e)->entries_next;
621         
622     if (!*e)
623         return NULL;
624
625     return flx_record_ref((*e)->record);
626 }
627
628 void flx_server_dump(flxServer *s, FILE *f) {
629     flxEntry *e;
630     g_assert(s);
631     g_assert(f);
632
633     fprintf(f, "\n;;; ZONE DUMP FOLLOWS ;;;\n");
634
635     for (e = s->entries; e; e = e->entries_next) {
636         gchar *t;
637
638         if (e->dead)
639             continue;
640         
641         t = flx_record_to_string(e->record);
642         fprintf(f, "%s ; iface=%i proto=%i\n", t, e->interface, e->protocol);
643         g_free(t);
644     }
645
646     flx_dump_caches(s->monitor, f);
647 }
648
649 void flx_server_add_ptr(
650     flxServer *s,
651     flxEntryGroup *g,
652     gint interface,
653     guchar protocol,
654     flxEntryFlags flags,
655     const gchar *name,
656     const gchar *dest) {
657
658     flxRecord *r;
659
660     g_assert(dest);
661
662     r = flx_record_new_full(name ? name : s->hostname, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_PTR);
663     r->data.ptr.name = flx_normalize_name(dest);
664     flx_server_add(s, g, interface, protocol, flags, r);
665     flx_record_unref(r);
666 }
667
668 void flx_server_add_address(
669     flxServer *s,
670     flxEntryGroup *g,
671     gint interface,
672     guchar protocol,
673     flxEntryFlags flags,
674     const gchar *name,
675     flxAddress *a) {
676
677     gchar *n = NULL;
678     g_assert(s);
679     g_assert(a);
680
681     name = name ? (n = flx_normalize_name(name)) : s->hostname;
682     
683     if (a->family == AF_INET) {
684         gchar *reverse;
685         flxRecord  *r;
686
687         r = flx_record_new_full(name, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_A);
688         r->data.a.address = a->data.ipv4;
689         flx_server_add(s, g, interface, protocol, flags, r);
690         flx_record_unref(r);
691         
692         reverse = flx_reverse_lookup_name_ipv4(&a->data.ipv4);
693         g_assert(reverse);
694         flx_server_add_ptr(s, g, interface, protocol, flags, reverse, name);
695         g_free(reverse);
696         
697     } else {
698         gchar *reverse;
699         flxRecord *r;
700             
701         r = flx_record_new_full(name, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_AAAA);
702         r->data.aaaa.address = a->data.ipv6;
703         flx_server_add(s, g, interface, protocol, flags, r);
704         flx_record_unref(r);
705
706         reverse = flx_reverse_lookup_name_ipv6_arpa(&a->data.ipv6);
707         g_assert(reverse);
708         flx_server_add_ptr(s, g, interface, protocol, flags, reverse, name);
709         g_free(reverse);
710     
711         reverse = flx_reverse_lookup_name_ipv6_int(&a->data.ipv6);
712         g_assert(reverse);
713         flx_server_add_ptr(s, g, interface, protocol, flags, reverse, name);
714         g_free(reverse);
715     }
716     
717     g_free(n);
718 }
719
720 void flx_server_add_text_strlst(
721     flxServer *s,
722     flxEntryGroup *g,
723     gint interface,
724     guchar protocol,
725     flxEntryFlags flags,
726     const gchar *name,
727     flxStringList *strlst) {
728
729     flxRecord *r;
730     
731     g_assert(s);
732     
733     r = flx_record_new_full(name ? name : s->hostname, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_TXT);
734     r->data.txt.string_list = strlst;
735     flx_server_add(s, g, interface, protocol, flags, r);
736     flx_record_unref(r);
737 }
738
739 void flx_server_add_text_va(
740     flxServer *s,
741     flxEntryGroup *g,
742     gint interface,
743     guchar protocol,
744     flxEntryFlags flags,
745     const gchar *name,
746     va_list va) {
747     
748     g_assert(s);
749
750     flx_server_add_text_strlst(s, g, interface, protocol, flags, name, flx_string_list_new_va(va));
751 }
752
753 void flx_server_add_text(
754     flxServer *s,
755     flxEntryGroup *g,
756     gint interface,
757     guchar protocol,
758     flxEntryFlags flags,
759     const gchar *name,
760     ...) {
761
762     va_list va;
763     
764     g_assert(s);
765
766     va_start(va, name);
767     flx_server_add_text_va(s, g, interface, protocol, flags, name, va);
768     va_end(va);
769 }
770
771 static void escape_service_name(gchar *d, guint size, const gchar *s) {
772     g_assert(d);
773     g_assert(size);
774     g_assert(s);
775
776     while (*s && size >= 2) {
777         if (*s == '.' || *s == '\\') {
778             if (size < 3)
779                 break;
780
781             *(d++) = '\\';
782             size--;
783         }
784             
785         *(d++) = *(s++);
786         size--;
787     }
788
789     g_assert(size > 0);
790     *(d++) = 0;
791 }
792
793 void flx_server_add_service_strlst(
794     flxServer *s,
795     flxEntryGroup *g,
796     gint interface,
797     guchar protocol,
798     const gchar *type,
799     const gchar *name,
800     const gchar *domain,
801     const gchar *host,
802     guint16 port,
803     flxStringList *strlst) {
804
805     gchar ptr_name[256], svc_name[256], ename[64], enum_ptr[256];
806     flxRecord *r;
807     
808     g_assert(s);
809     g_assert(type);
810     g_assert(name);
811
812     escape_service_name(ename, sizeof(ename), name);
813
814     if (domain) {
815         while (domain[0] == '.')
816             domain++;
817     } else
818         domain = "local";
819
820     if (!host)
821         host = s->hostname;
822
823     snprintf(ptr_name, sizeof(ptr_name), "%s.%s", type, domain);
824     snprintf(svc_name, sizeof(svc_name), "%s.%s.%s", ename, type, domain);
825     
826     flx_server_add_ptr(s, g, interface, protocol, FLX_ENTRY_NULL, ptr_name, svc_name);
827
828     r = flx_record_new_full(svc_name, FLX_DNS_CLASS_IN, FLX_DNS_TYPE_SRV);
829     r->data.srv.priority = 0;
830     r->data.srv.weight = 0;
831     r->data.srv.port = port;
832     r->data.srv.name = flx_normalize_name(host);
833     flx_server_add(s, g, interface, protocol, FLX_ENTRY_UNIQUE, r);
834     flx_record_unref(r);
835
836     flx_server_add_text_strlst(s, g, interface, protocol, FLX_ENTRY_UNIQUE, svc_name, strlst);
837
838     snprintf(enum_ptr, sizeof(enum_ptr), "_services._dns-sd._udp.%s", domain);
839     flx_server_add_ptr(s, g, interface, protocol, FLX_ENTRY_NULL, enum_ptr, ptr_name);
840 }
841
842 void flx_server_add_service_va(
843     flxServer *s,
844     flxEntryGroup *g,
845     gint interface,
846     guchar protocol,
847     const gchar *type,
848     const gchar *name,
849     const gchar *domain,
850     const gchar *host,
851     guint16 port,
852     va_list va){
853
854     g_assert(s);
855     g_assert(type);
856     g_assert(name);
857
858     flx_server_add_service(s, g, interface, protocol, type, name, domain, host, port, flx_string_list_new_va(va));
859 }
860
861 void flx_server_add_service(
862     flxServer *s,
863     flxEntryGroup *g,
864     gint interface,
865     guchar protocol,
866     const gchar *type,
867     const gchar *name,
868     const gchar *domain,
869     const gchar *host,
870     guint16 port,
871     ... ){
872
873     va_list va;
874     
875     g_assert(s);
876     g_assert(type);
877     g_assert(name);
878
879     va_start(va, port);
880     flx_server_add_service_va(s, g, interface, protocol, type, name, domain, host, port, va);
881     va_end(va);
882 }
883
884 static void post_query_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) {
885     flxKey *k = userdata;
886
887     g_assert(m);
888     g_assert(i);
889     g_assert(k);
890
891     flx_interface_post_query(i, k, FALSE);
892 }
893
894 void flx_server_post_query(flxServer *s, gint interface, guchar protocol, flxKey *key) {
895     g_assert(s);
896     g_assert(key);
897
898     flx_interface_monitor_walk(s->monitor, interface, protocol, post_query_callback, key);
899 }
900
901 struct tmpdata {
902     flxRecord *record;
903     gboolean flush_cache;
904 };
905
906 static void post_response_callback(flxInterfaceMonitor *m, flxInterface *i, gpointer userdata) {
907     struct tmpdata *tmpdata = userdata;
908
909     g_assert(m);
910     g_assert(i);
911     g_assert(tmpdata);
912
913     flx_interface_post_response(i, NULL, tmpdata->record, tmpdata->flush_cache, FALSE);
914 }
915
916 void flx_server_post_response(flxServer *s, gint interface, guchar protocol, flxRecord *record, gboolean flush_cache) {
917     struct tmpdata tmpdata;
918     
919     g_assert(s);
920     g_assert(record);
921
922     tmpdata.record = record;
923     tmpdata.flush_cache = flush_cache;
924
925     flx_interface_monitor_walk(s->monitor, interface, protocol, post_response_callback, &tmpdata);
926 }
927
928 void flx_entry_group_change_state(flxEntryGroup *g, flxEntryGroupState state) {
929     g_assert(g);
930
931     g->state = state;
932     
933     if (g->callback) {
934         g->callback(g->server, g, state, g->userdata);
935         return;
936     }
937 }
938
939 flxEntryGroup *flx_entry_group_new(flxServer *s, flxEntryGroupCallback callback, gpointer userdata) {
940     flxEntryGroup *g;
941     
942     g_assert(s);
943
944     g = g_new(flxEntryGroup, 1);
945     g->server = s;
946     g->callback = callback;
947     g->userdata = userdata;
948     g->dead = FALSE;
949     g->state = FLX_ENTRY_GROUP_UNCOMMITED;
950     g->n_probing = 0;
951     FLX_LLIST_HEAD_INIT(flxEntry, g->entries);
952
953     FLX_LLIST_PREPEND(flxEntryGroup, groups, s->groups, g);
954     return g;
955 }
956
957 void flx_entry_group_free(flxEntryGroup *g) {
958     g_assert(g);
959     g_assert(g->server);
960
961     g->dead = TRUE;
962     g->server->need_group_cleanup = TRUE;
963 }
964
965 void flx_entry_group_commit(flxEntryGroup *g) {
966     flxEntry *e;
967     
968     g_assert(g);
969     g_assert(!g->dead);
970
971     if (g->state != FLX_ENTRY_GROUP_UNCOMMITED)
972         return;
973
974     flx_entry_group_change_state(g, FLX_ENTRY_GROUP_REGISTERING);
975     flx_announce_group(g->server, g);
976     flx_entry_group_check_probed(g, FALSE);
977 }
978
979 gboolean flx_entry_commited(flxEntry *e) {
980     g_assert(e);
981     g_assert(!e->dead);
982
983     return !e->group ||
984         e->group->state == FLX_ENTRY_GROUP_REGISTERING ||
985         e->group->state == FLX_ENTRY_GROUP_ESTABLISHED;
986 }
987
988 flxEntryGroupState flx_entry_group_get_state(flxEntryGroup *g) {
989     g_assert(g);
990     g_assert(!g->dead);
991
992     return g->state;
993 }