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