]> git.meshlink.io Git - catta/blob - src/entry.c
cd3c6e2f7230218ba0b58edefb83868a1fed55fa
[catta] / src / entry.c
1 /***
2   This file is part of catta.
3
4   catta is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8
9   catta is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with catta; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <string.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <assert.h>
29 #include <stdlib.h>
30
31 #include <arpa/inet.h>
32
33 #include <sys/utsname.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36
37 #include <catta/domain.h>
38 #include <catta/timeval.h>
39 #include <catta/malloc.h>
40 #include <catta/error.h>
41 #include <catta/domain.h>
42 #include <catta/log.h>
43
44 #include "internal.h"
45 #include "iface.h"
46 #include "socket.h"
47 #include "browse.h"
48 #include "util.h"
49 #include "dns-srv-rr.h"
50 #include "rr-util.h"
51 #include "domain-util.h"
52
53 static void transport_flags_from_domain(CattaServer *s, CattaPublishFlags *flags, const char *domain) {
54     assert(flags);
55     assert(domain);
56
57     assert(!((*flags & CATTA_PUBLISH_USE_MULTICAST) && (*flags & CATTA_PUBLISH_USE_WIDE_AREA)));
58
59     if (*flags & (CATTA_PUBLISH_USE_MULTICAST|CATTA_PUBLISH_USE_WIDE_AREA))
60         return;
61
62     if (!s->wide_area_lookup_engine ||
63         !catta_wide_area_has_servers(s->wide_area_lookup_engine) ||
64         catta_domain_ends_with(domain, CATTA_MDNS_SUFFIX_LOCAL) ||
65         catta_domain_ends_with(domain, CATTA_MDNS_SUFFIX_ADDR_IPV4) ||
66         catta_domain_ends_with(domain, CATTA_MDNS_SUFFIX_ADDR_IPV6))
67         *flags |= CATTA_PUBLISH_USE_MULTICAST;
68     else
69         *flags |= CATTA_PUBLISH_USE_WIDE_AREA;
70 }
71
72 void catta_entry_free(CattaServer*s, CattaEntry *e) {
73     CattaEntry *t;
74
75     assert(s);
76     assert(e);
77
78     catta_goodbye_entry(s, e, 1, 1);
79
80     /* Remove from linked list */
81     CATTA_LLIST_REMOVE(CattaEntry, entries, s->entries, e);
82
83     /* Remove from hash table indexed by name */
84     t = catta_hashmap_lookup(s->entries_by_key, e->record->key);
85     CATTA_LLIST_REMOVE(CattaEntry, by_key, t, e);
86     if (t)
87         catta_hashmap_replace(s->entries_by_key, t->record->key, t);
88     else
89         catta_hashmap_remove(s->entries_by_key, e->record->key);
90
91     /* Remove from associated group */
92     if (e->group)
93         CATTA_LLIST_REMOVE(CattaEntry, by_group, e->group->entries, e);
94
95     catta_record_unref(e->record);
96     catta_free(e);
97 }
98
99 void catta_entry_group_free(CattaServer *s, CattaSEntryGroup *g) {
100     assert(s);
101     assert(g);
102
103     while (g->entries)
104         catta_entry_free(s, g->entries);
105
106     if (g->register_time_event)
107         catta_time_event_free(g->register_time_event);
108
109     CATTA_LLIST_REMOVE(CattaSEntryGroup, groups, s->groups, g);
110     catta_free(g);
111 }
112
113 void catta_cleanup_dead_entries(CattaServer *s) {
114     assert(s);
115
116     if (s->need_group_cleanup) {
117         CattaSEntryGroup *g, *next;
118
119         for (g = s->groups; g; g = next) {
120             next = g->groups_next;
121
122             if (g->dead)
123                 catta_entry_group_free(s, g);
124         }
125
126         s->need_group_cleanup = 0;
127     }
128
129     if (s->need_entry_cleanup) {
130         CattaEntry *e, *next;
131
132         for (e = s->entries; e; e = next) {
133             next = e->entries_next;
134
135             if (e->dead)
136                 catta_entry_free(s, e);
137         }
138
139         s->need_entry_cleanup = 0;
140     }
141
142     if (s->need_browser_cleanup)
143         catta_browser_cleanup(s);
144
145     if (s->cleanup_time_event) {
146         catta_time_event_free(s->cleanup_time_event);
147         s->cleanup_time_event = NULL;
148     }
149 }
150
151 static int check_record_conflict(CattaServer *s, CattaIfIndex iface, CattaProtocol protocol, CattaRecord *r, CattaPublishFlags flags) {
152     CattaEntry *e;
153
154     assert(s);
155     assert(r);
156
157     for (e = catta_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
158         if (e->dead)
159             continue;
160
161         if (!(flags & CATTA_PUBLISH_UNIQUE) && !(e->flags & CATTA_PUBLISH_UNIQUE))
162             continue;
163
164         if ((flags & CATTA_PUBLISH_ALLOW_MULTIPLE) && (e->flags & CATTA_PUBLISH_ALLOW_MULTIPLE) )
165             continue;
166
167         if (catta_record_equal_no_ttl(r, e->record)) {
168             /* The records are the same, not a conflict in any case */
169             continue;
170         }
171
172         if ((iface <= 0 ||
173              e->iface <= 0 ||
174              e->iface == iface) &&
175             (protocol == CATTA_PROTO_UNSPEC ||
176              e->protocol == CATTA_PROTO_UNSPEC ||
177              e->protocol == protocol))
178
179             return -1;
180     }
181
182     return 0;
183 }
184
185 static CattaEntry * server_add_internal(
186     CattaServer *s,
187     CattaSEntryGroup *g,
188     CattaIfIndex iface,
189     CattaProtocol protocol,
190     CattaPublishFlags flags,
191     CattaRecord *r) {
192
193     CattaEntry *e;
194
195     assert(s);
196     assert(r);
197
198     CATTA_CHECK_VALIDITY_RETURN_NULL(s, s->state != CATTA_SERVER_FAILURE && s->state != CATTA_SERVER_INVALID, CATTA_ERR_BAD_STATE);
199     CATTA_CHECK_VALIDITY_RETURN_NULL(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
200     CATTA_CHECK_VALIDITY_RETURN_NULL(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
201     CATTA_CHECK_VALIDITY_RETURN_NULL(s, CATTA_FLAGS_VALID(
202                                          flags,
203                                          CATTA_PUBLISH_NO_ANNOUNCE|
204                                          CATTA_PUBLISH_NO_PROBE|
205                                          CATTA_PUBLISH_UNIQUE|
206                                          CATTA_PUBLISH_ALLOW_MULTIPLE|
207                                          CATTA_PUBLISH_UPDATE|
208                                          CATTA_PUBLISH_USE_WIDE_AREA|
209                                          CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
210     CATTA_CHECK_VALIDITY_RETURN_NULL(s, catta_is_valid_domain_name(r->key->name), CATTA_ERR_INVALID_HOST_NAME);
211     CATTA_CHECK_VALIDITY_RETURN_NULL(s, r->ttl != 0, CATTA_ERR_INVALID_TTL);
212     CATTA_CHECK_VALIDITY_RETURN_NULL(s, !catta_key_is_pattern(r->key), CATTA_ERR_IS_PATTERN);
213     CATTA_CHECK_VALIDITY_RETURN_NULL(s, catta_record_is_valid(r), CATTA_ERR_INVALID_RECORD);
214     CATTA_CHECK_VALIDITY_RETURN_NULL(s, r->key->clazz == CATTA_DNS_CLASS_IN, CATTA_ERR_INVALID_DNS_CLASS);
215     CATTA_CHECK_VALIDITY_RETURN_NULL(s,
216                                      (r->key->type != 0) &&
217                                      (r->key->type != CATTA_DNS_TYPE_ANY) &&
218                                      (r->key->type != CATTA_DNS_TYPE_OPT) &&
219                                      (r->key->type != CATTA_DNS_TYPE_TKEY) &&
220                                      (r->key->type != CATTA_DNS_TYPE_TSIG) &&
221                                      (r->key->type != CATTA_DNS_TYPE_IXFR) &&
222                                      (r->key->type != CATTA_DNS_TYPE_AXFR), CATTA_ERR_INVALID_DNS_TYPE);
223
224     transport_flags_from_domain(s, &flags, r->key->name);
225     CATTA_CHECK_VALIDITY_RETURN_NULL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
226     CATTA_CHECK_VALIDITY_RETURN_NULL(s, !s->config.disable_publishing, CATTA_ERR_NOT_PERMITTED);
227     CATTA_CHECK_VALIDITY_RETURN_NULL(s,
228                                      !g ||
229                                      (g->state != CATTA_ENTRY_GROUP_ESTABLISHED && g->state != CATTA_ENTRY_GROUP_REGISTERING) ||
230                                      (flags & CATTA_PUBLISH_UPDATE), CATTA_ERR_BAD_STATE);
231
232     if (flags & CATTA_PUBLISH_UPDATE) {
233         CattaRecord *old_record;
234         int is_first = 1;
235
236         /* Update and existing record */
237
238         /* Find the first matching entry */
239         for (e = catta_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
240             if (!e->dead && e->group == g && e->iface == iface && e->protocol == protocol)
241                 break;
242
243             is_first = 0;
244         }
245
246         /* Hmm, nothing found? */
247         if (!e) {
248             catta_server_set_errno(s, CATTA_ERR_NOT_FOUND);
249             return NULL;
250         }
251
252         /* Update the entry */
253         old_record = e->record;
254         e->record = catta_record_ref(r);
255         e->flags = flags;
256
257         /* Announce our changes when needed */
258         if (!catta_record_equal_no_ttl(old_record, r) && (!g || g->state != CATTA_ENTRY_GROUP_UNCOMMITED)) {
259
260             /* Remove the old entry from all caches, if needed */
261             if (!(e->flags & CATTA_PUBLISH_UNIQUE))
262                 catta_goodbye_entry(s, e, 1, 0);
263
264             /* Reannounce our updated entry */
265             catta_reannounce_entry(s, e);
266         }
267
268         /* If we were the first entry in the list, we need to update the key */
269         if (is_first)
270             catta_hashmap_replace(s->entries_by_key, e->record->key, e);
271
272         catta_record_unref(old_record);
273
274     } else {
275         CattaEntry *t;
276
277         /* Add a new record */
278
279         if (check_record_conflict(s, iface, protocol, r, flags) < 0) {
280             catta_server_set_errno(s, CATTA_ERR_COLLISION);
281             return NULL;
282         }
283
284         if (!(e = catta_new(CattaEntry, 1))) {
285             catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
286             return NULL;
287         }
288
289         e->server = s;
290         e->record = catta_record_ref(r);
291         e->group = g;
292         e->iface = iface;
293         e->protocol = protocol;
294         e->flags = flags;
295         e->dead = 0;
296
297         CATTA_LLIST_HEAD_INIT(CattaAnnouncer, e->announcers);
298
299         CATTA_LLIST_PREPEND(CattaEntry, entries, s->entries, e);
300
301         /* Insert into hash table indexed by name */
302         t = catta_hashmap_lookup(s->entries_by_key, e->record->key);
303         CATTA_LLIST_PREPEND(CattaEntry, by_key, t, e);
304         catta_hashmap_replace(s->entries_by_key, e->record->key, t);
305
306         /* Insert into group list */
307         if (g)
308             CATTA_LLIST_PREPEND(CattaEntry, by_group, g->entries, e);
309
310         catta_announce_entry(s, e);
311     }
312
313     return e;
314 }
315
316 int catta_server_add(
317     CattaServer *s,
318     CattaSEntryGroup *g,
319     CattaIfIndex iface,
320     CattaProtocol protocol,
321     CattaPublishFlags flags,
322     CattaRecord *r) {
323
324     if (!server_add_internal(s, g, iface, protocol, flags, r))
325         return catta_server_errno(s);
326
327     return CATTA_OK;
328 }
329
330 const CattaRecord *catta_server_iterate(CattaServer *s, CattaSEntryGroup *g, void **state) {
331     CattaEntry **e = (CattaEntry**) state;
332     assert(s);
333     assert(e);
334
335     if (!*e)
336         *e = g ? g->entries : s->entries;
337
338     while (*e && (*e)->dead)
339         *e = g ? (*e)->by_group_next : (*e)->entries_next;
340
341     if (!*e)
342         return NULL;
343
344     return catta_record_ref((*e)->record);
345 }
346
347 int catta_server_dump(CattaServer *s, CattaDumpCallback callback, void* userdata) {
348     CattaEntry *e;
349
350     assert(s);
351     assert(callback);
352
353     callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
354
355     for (e = s->entries; e; e = e->entries_next) {
356         char *t;
357         char ln[256];
358
359         if (e->dead)
360             continue;
361
362         if (!(t = catta_record_to_string(e->record)))
363             return catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
364
365         snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->iface, e->protocol);
366         catta_free(t);
367
368         callback(ln, userdata);
369     }
370
371     catta_dump_caches(s->monitor, callback, userdata);
372
373     if (s->wide_area_lookup_engine)
374         catta_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata);
375     return CATTA_OK;
376 }
377
378 static CattaEntry *server_add_ptr_internal(
379     CattaServer *s,
380     CattaSEntryGroup *g,
381     CattaIfIndex iface,
382     CattaProtocol protocol,
383     CattaPublishFlags flags,
384     uint32_t ttl,
385     const char *name,
386     const char *dest) {
387
388     CattaRecord *r;
389     CattaEntry *e;
390
391     assert(s);
392     assert(dest);
393
394     CATTA_CHECK_VALIDITY_RETURN_NULL(s, !name || catta_is_valid_domain_name(name), CATTA_ERR_INVALID_HOST_NAME);
395     CATTA_CHECK_VALIDITY_RETURN_NULL(s, catta_is_valid_domain_name(dest), CATTA_ERR_INVALID_HOST_NAME);
396
397     if (!name)
398         name = s->host_name_fqdn;
399
400     if (!(r = catta_record_new_full(name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_PTR, ttl))) {
401         catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
402         return NULL;
403     }
404
405     r->data.ptr.name = catta_normalize_name_strdup(dest);
406     e = server_add_internal(s, g, iface, protocol, flags, r);
407     catta_record_unref(r);
408     return e;
409 }
410
411 int catta_server_add_ptr(
412     CattaServer *s,
413     CattaSEntryGroup *g,
414     CattaIfIndex iface,
415     CattaProtocol protocol,
416     CattaPublishFlags flags,
417     uint32_t ttl,
418     const char *name,
419     const char *dest) {
420
421     CattaEntry *e;
422
423     assert(s);
424
425     if (!(e = server_add_ptr_internal(s, g, iface, protocol, flags, ttl, name, dest)))
426         return catta_server_errno(s);
427
428     return CATTA_OK;
429 }
430
431 int catta_server_add_address(
432     CattaServer *s,
433     CattaSEntryGroup *g,
434     CattaIfIndex iface,
435     CattaProtocol protocol,
436     CattaPublishFlags flags,
437     const char *name,
438     CattaAddress *a) {
439
440     char n[CATTA_DOMAIN_NAME_MAX];
441     int ret = CATTA_OK;
442     CattaEntry *entry = NULL, *reverse = NULL;
443     CattaRecord  *r;
444
445     assert(s);
446     assert(a);
447
448     CATTA_CHECK_VALIDITY(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
449     CATTA_CHECK_VALIDITY(s, CATTA_PROTO_VALID(protocol) && CATTA_PROTO_VALID(a->proto), CATTA_ERR_INVALID_PROTOCOL);
450     CATTA_CHECK_VALIDITY(s, CATTA_FLAGS_VALID(flags,
451                                               CATTA_PUBLISH_NO_REVERSE|
452                                               CATTA_PUBLISH_NO_ANNOUNCE|
453                                               CATTA_PUBLISH_NO_PROBE|
454                                               CATTA_PUBLISH_UPDATE|
455                                               CATTA_PUBLISH_USE_WIDE_AREA|
456                                               CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
457     CATTA_CHECK_VALIDITY(s, !name || catta_is_valid_fqdn(name), CATTA_ERR_INVALID_HOST_NAME);
458
459     /* Prepare the host naem */
460
461     if (!name)
462         name = s->host_name_fqdn;
463     else {
464         CATTA_ASSERT_TRUE(catta_normalize_name(name, n, sizeof(n)));
465         name = n;
466     }
467
468     transport_flags_from_domain(s, &flags, name);
469     CATTA_CHECK_VALIDITY(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
470
471     /* Create the A/AAAA record */
472
473     if (a->proto == CATTA_PROTO_INET) {
474
475         if (!(r = catta_record_new_full(name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_A, CATTA_DEFAULT_TTL_HOST_NAME))) {
476             ret = catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
477             goto finish;
478         }
479
480         r->data.a.address = a->data.ipv4;
481
482     } else {
483         assert(a->proto == CATTA_PROTO_INET6);
484
485         if (!(r = catta_record_new_full(name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_AAAA, CATTA_DEFAULT_TTL_HOST_NAME))) {
486             ret = catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
487             goto finish;
488         }
489
490         r->data.aaaa.address = a->data.ipv6;
491     }
492
493     entry = server_add_internal(s, g, iface, protocol, (flags & ~ CATTA_PUBLISH_NO_REVERSE) | CATTA_PUBLISH_UNIQUE | CATTA_PUBLISH_ALLOW_MULTIPLE, r);
494     catta_record_unref(r);
495
496     if (!entry) {
497         ret = catta_server_errno(s);
498         goto finish;
499     }
500
501     /* Create the reverse lookup entry */
502
503     if (!(flags & CATTA_PUBLISH_NO_REVERSE)) {
504         char reverse_n[CATTA_DOMAIN_NAME_MAX];
505         catta_reverse_lookup_name(a, reverse_n, sizeof(reverse_n));
506
507         if (!(reverse = server_add_ptr_internal(s, g, iface, protocol, flags | CATTA_PUBLISH_UNIQUE, CATTA_DEFAULT_TTL_HOST_NAME, reverse_n, name))) {
508             ret = catta_server_errno(s);
509             goto finish;
510         }
511     }
512
513 finish:
514
515     if (ret != CATTA_OK && !(flags & CATTA_PUBLISH_UPDATE)) {
516         if (entry)
517             catta_entry_free(s, entry);
518         if (reverse)
519             catta_entry_free(s, reverse);
520     }
521
522     return ret;
523 }
524
525 static CattaEntry *server_add_txt_strlst_nocopy(
526     CattaServer *s,
527     CattaSEntryGroup *g,
528     CattaIfIndex iface,
529     CattaProtocol protocol,
530     CattaPublishFlags flags,
531     uint32_t ttl,
532     const char *name,
533     CattaStringList *strlst) {
534
535     CattaRecord *r;
536     CattaEntry *e;
537
538     assert(s);
539
540     if (!(r = catta_record_new_full(name ? name : s->host_name_fqdn, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_TXT, ttl))) {
541         catta_string_list_free(strlst);
542         catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
543         return NULL;
544     }
545
546     r->data.txt.string_list = strlst;
547     e = server_add_internal(s, g, iface, protocol, flags, r);
548     catta_record_unref(r);
549
550     return e;
551 }
552
553 static CattaStringList *add_magic_cookie(
554     CattaServer *s,
555     CattaStringList *strlst) {
556
557     assert(s);
558
559     if (!s->config.add_service_cookie)
560         return strlst;
561
562     if (catta_string_list_find(strlst, CATTA_SERVICE_COOKIE))
563         /* This string list already contains a magic cookie */
564         return strlst;
565
566     return catta_string_list_add_printf(strlst, CATTA_SERVICE_COOKIE"=%u", s->local_service_cookie);
567 }
568
569 static int server_add_service_strlst_nocopy(
570     CattaServer *s,
571     CattaSEntryGroup *g,
572     CattaIfIndex iface,
573     CattaProtocol protocol,
574     CattaPublishFlags flags,
575     const char *name,
576     const char *type,
577     const char *domain,
578     const char *host,
579     uint16_t port,
580     CattaStringList *strlst) {
581
582     char ptr_name[CATTA_DOMAIN_NAME_MAX], svc_name[CATTA_DOMAIN_NAME_MAX], enum_ptr[CATTA_DOMAIN_NAME_MAX], *h = NULL;
583     CattaRecord *r = NULL;
584     int ret = CATTA_OK;
585     CattaEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL;
586
587     assert(s);
588     assert(type);
589     assert(name);
590
591     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
592     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
593     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_FLAGS_VALID(flags,
594                                                                 CATTA_PUBLISH_NO_COOKIE|
595                                                                 CATTA_PUBLISH_UPDATE|
596                                                                 CATTA_PUBLISH_USE_WIDE_AREA|
597                                                                 CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
598     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_name(name), CATTA_ERR_INVALID_SERVICE_NAME);
599     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_type_strict(type), CATTA_ERR_INVALID_SERVICE_TYPE);
600     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
601     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || catta_is_valid_fqdn(host), CATTA_ERR_INVALID_HOST_NAME);
602
603     if (!domain)
604         domain = s->domain_name;
605
606     if (!host)
607         host = s->host_name_fqdn;
608
609     transport_flags_from_domain(s, &flags, domain);
610     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
611
612     if (!(h = catta_normalize_name_strdup(host))) {
613         ret = catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
614         goto fail;
615     }
616
617     if ((ret = catta_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
618         (ret = catta_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
619         (ret = catta_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
620         catta_server_set_errno(s, ret);
621         goto fail;
622     }
623
624     /* Add service enumeration PTR record */
625
626     if (!(ptr_entry = server_add_ptr_internal(s, g, iface, protocol, 0, CATTA_DEFAULT_TTL, ptr_name, svc_name))) {
627         ret = catta_server_errno(s);
628         goto fail;
629     }
630
631     /* Add SRV record */
632
633     if (!(r = catta_record_new_full(svc_name, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_SRV, CATTA_DEFAULT_TTL_HOST_NAME))) {
634         ret = catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
635         goto fail;
636     }
637
638     r->data.srv.priority = 0;
639     r->data.srv.weight = 0;
640     r->data.srv.port = port;
641     r->data.srv.name = h;
642     h = NULL;
643     srv_entry = server_add_internal(s, g, iface, protocol, CATTA_PUBLISH_UNIQUE, r);
644     catta_record_unref(r);
645
646     if (!srv_entry) {
647         ret = catta_server_errno(s);
648         goto fail;
649     }
650
651     /* Add TXT record */
652
653     if (!(flags & CATTA_PUBLISH_NO_COOKIE))
654         strlst = add_magic_cookie(s, strlst);
655
656     txt_entry = server_add_txt_strlst_nocopy(s, g, iface, protocol, CATTA_PUBLISH_UNIQUE, CATTA_DEFAULT_TTL, svc_name, strlst);
657     strlst = NULL;
658
659     if (!txt_entry) {
660         ret = catta_server_errno(s);
661         goto fail;
662     }
663
664     /* Add service type enumeration record */
665
666     if (!(enum_entry = server_add_ptr_internal(s, g, iface, protocol, 0, CATTA_DEFAULT_TTL, enum_ptr, ptr_name))) {
667         ret = catta_server_errno(s);
668         goto fail;
669     }
670
671 fail:
672     if (ret != CATTA_OK && !(flags & CATTA_PUBLISH_UPDATE)) {
673         if (srv_entry)
674             catta_entry_free(s, srv_entry);
675         if (txt_entry)
676             catta_entry_free(s, txt_entry);
677         if (ptr_entry)
678             catta_entry_free(s, ptr_entry);
679         if (enum_entry)
680             catta_entry_free(s, enum_entry);
681     }
682
683     catta_string_list_free(strlst);
684     catta_free(h);
685
686     return ret;
687 }
688
689 int catta_server_add_service_strlst(
690     CattaServer *s,
691     CattaSEntryGroup *g,
692     CattaIfIndex iface,
693     CattaProtocol protocol,
694     CattaPublishFlags flags,
695     const char *name,
696     const char *type,
697     const char *domain,
698     const char *host,
699     uint16_t port,
700     CattaStringList *strlst) {
701
702     assert(s);
703     assert(type);
704     assert(name);
705
706     return server_add_service_strlst_nocopy(s, g, iface, protocol, flags, name, type, domain, host, port, catta_string_list_copy(strlst));
707 }
708
709 int catta_server_add_service(
710     CattaServer *s,
711     CattaSEntryGroup *g,
712     CattaIfIndex iface,
713     CattaProtocol protocol,
714     CattaPublishFlags flags,
715     const char *name,
716     const char *type,
717     const char *domain,
718     const char *host,
719     uint16_t port,
720     ... ){
721
722     va_list va;
723     int ret;
724
725     va_start(va, port);
726     ret = server_add_service_strlst_nocopy(s, g, iface, protocol, flags, name, type, domain, host, port, catta_string_list_new_va(va));
727     va_end(va);
728
729     return ret;
730 }
731
732 static int server_update_service_txt_strlst_nocopy(
733     CattaServer *s,
734     CattaSEntryGroup *g,
735     CattaIfIndex iface,
736     CattaProtocol protocol,
737     CattaPublishFlags flags,
738     const char *name,
739     const char *type,
740     const char *domain,
741     CattaStringList *strlst) {
742
743     char svc_name[CATTA_DOMAIN_NAME_MAX];
744     int ret = CATTA_OK;
745     CattaEntry *e;
746
747     assert(s);
748     assert(type);
749     assert(name);
750
751     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
752     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
753     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_FLAGS_VALID(flags,
754                                                                 CATTA_PUBLISH_NO_COOKIE|
755                                                                 CATTA_PUBLISH_USE_WIDE_AREA|
756                                                                 CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
757     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_name(name), CATTA_ERR_INVALID_SERVICE_NAME);
758     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_type_strict(type), CATTA_ERR_INVALID_SERVICE_TYPE);
759     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
760
761     if (!domain)
762         domain = s->domain_name;
763
764     transport_flags_from_domain(s, &flags, domain);
765     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
766
767     if ((ret = catta_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) {
768         catta_server_set_errno(s, ret);
769         goto fail;
770     }
771
772     /* Add TXT record */
773     if (!(flags & CATTA_PUBLISH_NO_COOKIE))
774         strlst = add_magic_cookie(s, strlst);
775
776     e = server_add_txt_strlst_nocopy(s, g, iface, protocol, CATTA_PUBLISH_UNIQUE | CATTA_PUBLISH_UPDATE, CATTA_DEFAULT_TTL, svc_name, strlst);
777     strlst = NULL;
778
779     if (!e)
780         ret = catta_server_errno(s);
781
782 fail:
783
784     catta_string_list_free(strlst);
785
786     return ret;
787 }
788
789 int catta_server_update_service_txt_strlst(
790     CattaServer *s,
791     CattaSEntryGroup *g,
792     CattaIfIndex iface,
793     CattaProtocol protocol,
794     CattaPublishFlags flags,
795     const char *name,
796     const char *type,
797     const char *domain,
798     CattaStringList *strlst) {
799
800     return server_update_service_txt_strlst_nocopy(s, g, iface, protocol, flags, name, type, domain, catta_string_list_copy(strlst));
801 }
802
803 /** Update the TXT record for a service with the NULL termonate list of strings */
804 int catta_server_update_service_txt(
805     CattaServer *s,
806     CattaSEntryGroup *g,
807     CattaIfIndex iface,
808     CattaProtocol protocol,
809     CattaPublishFlags flags,
810     const char *name,
811     const char *type,
812     const char *domain,
813     ...) {
814
815     va_list va;
816     int ret;
817
818     va_start(va, domain);
819     ret = server_update_service_txt_strlst_nocopy(s, g, iface, protocol, flags, name, type, domain, catta_string_list_new_va(va));
820     va_end(va);
821
822     return ret;
823 }
824
825 int catta_server_add_service_subtype(
826     CattaServer *s,
827     CattaSEntryGroup *g,
828     CattaIfIndex iface,
829     CattaProtocol protocol,
830     CattaPublishFlags flags,
831     const char *name,
832     const char *type,
833     const char *domain,
834     const char *subtype) {
835
836     int ret = CATTA_OK;
837     char svc_name[CATTA_DOMAIN_NAME_MAX], ptr_name[CATTA_DOMAIN_NAME_MAX];
838
839     assert(name);
840     assert(type);
841     assert(subtype);
842
843     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
844     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_PROTO_VALID(protocol), CATTA_ERR_INVALID_PROTOCOL);
845     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, CATTA_FLAGS_VALID(flags, CATTA_PUBLISH_USE_MULTICAST|CATTA_PUBLISH_USE_WIDE_AREA), CATTA_ERR_INVALID_FLAGS);
846     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_name(name), CATTA_ERR_INVALID_SERVICE_NAME);
847     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_type_strict(type), CATTA_ERR_INVALID_SERVICE_TYPE);
848     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
849     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, catta_is_valid_service_subtype(subtype), CATTA_ERR_INVALID_SERVICE_SUBTYPE);
850
851     if (!domain)
852         domain = s->domain_name;
853
854     transport_flags_from_domain(s, &flags, domain);
855     CATTA_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
856
857     if ((ret = catta_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
858         (ret = catta_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) {
859         catta_server_set_errno(s, ret);
860         goto fail;
861     }
862
863     if ((ret = catta_server_add_ptr(s, g, iface, protocol, 0, CATTA_DEFAULT_TTL, ptr_name, svc_name)) < 0)
864         goto fail;
865
866 fail:
867
868     return ret;
869 }
870
871 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
872     static const char hex[] = "0123456789abcdef";
873     int b = 0;
874     const uint8_t *k = p;
875
876     while (sl > 1 && pl > 0) {
877         *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
878
879         if (b) {
880             k++;
881             pl--;
882         }
883
884         b = !b;
885
886         sl--;
887     }
888
889     if (sl > 0)
890         *s = 0;
891 }
892
893 static CattaEntry *server_add_dns_server_name(
894     CattaServer *s,
895     CattaSEntryGroup *g,
896     CattaIfIndex iface,
897     CattaProtocol protocol,
898     CattaPublishFlags flags,
899     const char *domain,
900     CattaDNSServerType type,
901     const char *name,
902     uint16_t port /** should be 53 */) {
903
904     CattaEntry *e;
905     char t[CATTA_DOMAIN_NAME_MAX], normalized_d[CATTA_DOMAIN_NAME_MAX], *n;
906
907     CattaRecord *r;
908
909     assert(s);
910     assert(name);
911
912     CATTA_CHECK_VALIDITY_RETURN_NULL(s, CATTA_FLAGS_VALID(flags, CATTA_PUBLISH_USE_WIDE_AREA|CATTA_PUBLISH_USE_MULTICAST), CATTA_ERR_INVALID_FLAGS);
913     CATTA_CHECK_VALIDITY_RETURN_NULL(s, type == CATTA_DNS_SERVER_UPDATE || type == CATTA_DNS_SERVER_RESOLVE, CATTA_ERR_INVALID_FLAGS);
914     CATTA_CHECK_VALIDITY_RETURN_NULL(s, port != 0, CATTA_ERR_INVALID_PORT);
915     CATTA_CHECK_VALIDITY_RETURN_NULL(s, catta_is_valid_fqdn(name), CATTA_ERR_INVALID_HOST_NAME);
916     CATTA_CHECK_VALIDITY_RETURN_NULL(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
917
918     if (!domain)
919         domain = s->domain_name;
920
921     transport_flags_from_domain(s, &flags, domain);
922     CATTA_CHECK_VALIDITY_RETURN_NULL(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
923
924     if (!(n = catta_normalize_name_strdup(name))) {
925         catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
926         return NULL;
927     }
928
929     CATTA_ASSERT_TRUE(catta_normalize_name(domain, normalized_d, sizeof(normalized_d)));
930
931     snprintf(t, sizeof(t), "%s.%s", type == CATTA_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d);
932
933     if (!(r = catta_record_new_full(t, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_SRV, CATTA_DEFAULT_TTL_HOST_NAME))) {
934         catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
935         catta_free(n);
936         return NULL;
937     }
938
939     r->data.srv.priority = 0;
940     r->data.srv.weight = 0;
941     r->data.srv.port = port;
942     r->data.srv.name = n;
943     e = server_add_internal(s, g, iface, protocol, 0, r);
944     catta_record_unref(r);
945
946     return e;
947 }
948
949 int catta_server_add_dns_server_address(
950     CattaServer *s,
951     CattaSEntryGroup *g,
952     CattaIfIndex iface,
953     CattaProtocol protocol,
954     CattaPublishFlags flags,
955     const char *domain,
956     CattaDNSServerType type,
957     const CattaAddress *address,
958     uint16_t port /** should be 53 */) {
959
960     CattaRecord *r;
961     char n[64], h[64];
962     CattaEntry *a_entry, *s_entry;
963
964     assert(s);
965     assert(address);
966
967     CATTA_CHECK_VALIDITY(s, CATTA_IF_VALID(iface), CATTA_ERR_INVALID_INTERFACE);
968     CATTA_CHECK_VALIDITY(s, CATTA_PROTO_VALID(protocol) && CATTA_PROTO_VALID(address->proto), CATTA_ERR_INVALID_PROTOCOL);
969     CATTA_CHECK_VALIDITY(s, CATTA_FLAGS_VALID(flags, CATTA_PUBLISH_USE_MULTICAST|CATTA_PUBLISH_USE_WIDE_AREA), CATTA_ERR_INVALID_FLAGS);
970     CATTA_CHECK_VALIDITY(s, type == CATTA_DNS_SERVER_UPDATE || type == CATTA_DNS_SERVER_RESOLVE, CATTA_ERR_INVALID_FLAGS);
971     CATTA_CHECK_VALIDITY(s, port != 0, CATTA_ERR_INVALID_PORT);
972     CATTA_CHECK_VALIDITY(s, !domain || catta_is_valid_domain_name(domain), CATTA_ERR_INVALID_DOMAIN_NAME);
973
974     if (!domain)
975         domain = s->domain_name;
976
977     transport_flags_from_domain(s, &flags, domain);
978     CATTA_CHECK_VALIDITY(s, flags & CATTA_PUBLISH_USE_MULTICAST, CATTA_ERR_NOT_SUPPORTED);
979
980     if (address->proto == CATTA_PROTO_INET) {
981         hexstring(h, sizeof(h), &address->data, sizeof(CattaIPv4Address));
982         snprintf(n, sizeof(n), "ip-%s.%s", h, domain);
983         r = catta_record_new_full(n, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_A, CATTA_DEFAULT_TTL_HOST_NAME);
984         r->data.a.address = address->data.ipv4;
985     } else {
986         hexstring(h, sizeof(h), &address->data, sizeof(CattaIPv6Address));
987         snprintf(n, sizeof(n), "ip6-%s.%s", h, domain);
988         r = catta_record_new_full(n, CATTA_DNS_CLASS_IN, CATTA_DNS_TYPE_AAAA, CATTA_DEFAULT_TTL_HOST_NAME);
989         r->data.aaaa.address = address->data.ipv6;
990     }
991
992     if (!r)
993         return catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
994
995     a_entry = server_add_internal(s, g, iface, protocol, CATTA_PUBLISH_UNIQUE | CATTA_PUBLISH_ALLOW_MULTIPLE, r);
996     catta_record_unref(r);
997
998     if (!a_entry)
999         return catta_server_errno(s);
1000
1001     if (!(s_entry = server_add_dns_server_name(s, g, iface, protocol, flags, domain, type, n, port))) {
1002         if (!(flags & CATTA_PUBLISH_UPDATE))
1003             catta_entry_free(s, a_entry);
1004         return catta_server_errno(s);
1005     }
1006
1007     return CATTA_OK;
1008 }
1009
1010 void catta_s_entry_group_change_state(CattaSEntryGroup *g, CattaEntryGroupState state) {
1011     assert(g);
1012
1013     if (g->state == state)
1014         return;
1015
1016     assert(state <= CATTA_ENTRY_GROUP_COLLISION);
1017
1018     if (g->state == CATTA_ENTRY_GROUP_ESTABLISHED) {
1019
1020         /* If the entry group was established for a time longer then
1021          * 5s, reset the establishment trial counter */
1022
1023         if (catta_age(&g->established_at) > 5000000)
1024             g->n_register_try = 0;
1025     } else if (g->state == CATTA_ENTRY_GROUP_REGISTERING) {
1026         if (g->register_time_event) {
1027             catta_time_event_free(g->register_time_event);
1028             g->register_time_event = NULL;
1029         }
1030     }
1031
1032     if (state == CATTA_ENTRY_GROUP_ESTABLISHED)
1033
1034         /* If the entry group is now established, remember the time
1035          * this happened */
1036
1037         gettimeofday(&g->established_at, NULL);
1038
1039     g->state = state;
1040
1041     if (g->callback)
1042         g->callback(g->server, g, state, g->userdata);
1043 }
1044
1045 CattaSEntryGroup *catta_s_entry_group_new(CattaServer *s, CattaSEntryGroupCallback callback, void* userdata) {
1046     CattaSEntryGroup *g;
1047
1048     assert(s);
1049
1050     if (!(g = catta_new(CattaSEntryGroup, 1))) {
1051         catta_server_set_errno(s, CATTA_ERR_NO_MEMORY);
1052         return NULL;
1053     }
1054
1055     g->server = s;
1056     g->callback = callback;
1057     g->userdata = userdata;
1058     g->dead = 0;
1059     g->state = CATTA_ENTRY_GROUP_UNCOMMITED;
1060     g->n_probing = 0;
1061     g->n_register_try = 0;
1062     g->register_time_event = NULL;
1063     g->register_time.tv_sec = 0;
1064     g->register_time.tv_usec = 0;
1065     CATTA_LLIST_HEAD_INIT(CattaEntry, g->entries);
1066
1067     CATTA_LLIST_PREPEND(CattaSEntryGroup, groups, s->groups, g);
1068     return g;
1069 }
1070
1071 static void cleanup_time_event_callback(CATTA_GCC_UNUSED CattaTimeEvent *e, void* userdata) {
1072     CattaServer *s = userdata;
1073
1074     assert(s);
1075
1076     catta_cleanup_dead_entries(s);
1077 }
1078
1079 static void schedule_cleanup(CattaServer *s) {
1080     struct timeval tv;
1081
1082     assert(s);
1083
1084     if (!s->cleanup_time_event)
1085         s->cleanup_time_event = catta_time_event_new(s->time_event_queue, catta_elapse_time(&tv, 1000, 0), &cleanup_time_event_callback, s);
1086 }
1087
1088 void catta_s_entry_group_free(CattaSEntryGroup *g) {
1089     CattaEntry *e;
1090
1091     assert(g);
1092     assert(g->server);
1093
1094     for (e = g->entries; e; e = e->by_group_next) {
1095         if (!e->dead) {
1096             catta_goodbye_entry(g->server, e, 1, 1);
1097             e->dead = 1;
1098         }
1099     }
1100
1101     if (g->register_time_event) {
1102         catta_time_event_free(g->register_time_event);
1103         g->register_time_event = NULL;
1104     }
1105
1106     g->dead = 1;
1107
1108     g->server->need_group_cleanup = 1;
1109     g->server->need_entry_cleanup = 1;
1110
1111     schedule_cleanup(g->server);
1112 }
1113
1114 static void entry_group_commit_real(CattaSEntryGroup *g) {
1115     assert(g);
1116
1117     gettimeofday(&g->register_time, NULL);
1118
1119     catta_s_entry_group_change_state(g, CATTA_ENTRY_GROUP_REGISTERING);
1120
1121     if (g->dead)
1122         return;
1123
1124     catta_announce_group(g->server, g);
1125     catta_s_entry_group_check_probed(g, 0);
1126 }
1127
1128 static void entry_group_register_time_event_callback(CATTA_GCC_UNUSED CattaTimeEvent *e, void* userdata) {
1129     CattaSEntryGroup *g = userdata;
1130     assert(g);
1131
1132     catta_time_event_free(g->register_time_event);
1133     g->register_time_event = NULL;
1134
1135     /* Holdoff time passed, so let's start probing */
1136     entry_group_commit_real(g);
1137 }
1138
1139 int catta_s_entry_group_commit(CattaSEntryGroup *g) {
1140     struct timeval now;
1141
1142     assert(g);
1143     assert(!g->dead);
1144
1145     if (g->state != CATTA_ENTRY_GROUP_UNCOMMITED && g->state != CATTA_ENTRY_GROUP_COLLISION)
1146         return catta_server_set_errno(g->server, CATTA_ERR_BAD_STATE);
1147
1148     if (catta_s_entry_group_is_empty(g))
1149         return catta_server_set_errno(g->server, CATTA_ERR_IS_EMPTY);
1150
1151     g->n_register_try++;
1152
1153     catta_timeval_add(&g->register_time,
1154                       1000*(g->n_register_try >= CATTA_RR_RATE_LIMIT_COUNT ?
1155                             CATTA_RR_HOLDOFF_MSEC_RATE_LIMIT :
1156                             CATTA_RR_HOLDOFF_MSEC));
1157
1158     gettimeofday(&now, NULL);
1159
1160     if (catta_timeval_compare(&g->register_time, &now) <= 0) {
1161
1162         /* Holdoff time passed, so let's start probing */
1163         entry_group_commit_real(g);
1164     } else {
1165
1166          /* Holdoff time has not yet passed, so let's wait */
1167         assert(!g->register_time_event);
1168         g->register_time_event = catta_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
1169
1170         catta_s_entry_group_change_state(g, CATTA_ENTRY_GROUP_REGISTERING);
1171     }
1172
1173     return CATTA_OK;
1174 }
1175
1176 void catta_s_entry_group_reset(CattaSEntryGroup *g) {
1177     CattaEntry *e;
1178     assert(g);
1179
1180     for (e = g->entries; e; e = e->by_group_next) {
1181         if (!e->dead) {
1182             catta_goodbye_entry(g->server, e, 1, 1);
1183             e->dead = 1;
1184         }
1185     }
1186     g->server->need_entry_cleanup = 1;
1187
1188     g->n_probing = 0;
1189
1190     catta_s_entry_group_change_state(g, CATTA_ENTRY_GROUP_UNCOMMITED);
1191
1192     schedule_cleanup(g->server);
1193 }
1194
1195 int catta_entry_is_commited(CattaEntry *e) {
1196     assert(e);
1197     assert(!e->dead);
1198
1199     return !e->group ||
1200         e->group->state == CATTA_ENTRY_GROUP_REGISTERING ||
1201         e->group->state == CATTA_ENTRY_GROUP_ESTABLISHED;
1202 }
1203
1204 CattaEntryGroupState catta_s_entry_group_get_state(CattaSEntryGroup *g) {
1205     assert(g);
1206     assert(!g->dead);
1207
1208     return g->state;
1209 }
1210
1211 void catta_s_entry_group_set_data(CattaSEntryGroup *g, void* userdata) {
1212     assert(g);
1213
1214     g->userdata = userdata;
1215 }
1216
1217 void* catta_s_entry_group_get_data(CattaSEntryGroup *g) {
1218     assert(g);
1219
1220     return g->userdata;
1221 }
1222
1223 int catta_s_entry_group_is_empty(CattaSEntryGroup *g) {
1224     CattaEntry *e;
1225     assert(g);
1226
1227     /* Look for an entry that is not dead */
1228     for (e = g->entries; e; e = e->by_group_next)
1229         if (!e->dead)
1230             return 0;
1231
1232     return 1;
1233 }