]> git.meshlink.io Git - catta/blob - avahi-core/entry.c
81ce1cd2ccd555a39e5e914b963cdf0824cb686b
[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,
220                                      !g ||
221                                      (g->state != AVAHI_ENTRY_GROUP_ESTABLISHED && g->state != AVAHI_ENTRY_GROUP_REGISTERING) ||
222                                      (flags & AVAHI_PUBLISH_UPDATE), AVAHI_ERR_BAD_STATE);
223     
224     if (flags & AVAHI_PUBLISH_UPDATE) {
225         AvahiRecord *old_record;
226         int is_first = 1;
227         
228         /* Update and existing record */
229
230         /* Find the first matching entry */
231         for (e = avahi_hashmap_lookup(s->entries_by_key, r->key); e; e = e->by_key_next) {
232             if (!e->dead && e->group == g && e->interface == interface && e->protocol == protocol)
233                 break;
234
235             is_first = 0;
236         }
237
238         /* Hmm, nothing found? */
239         if (!e) {
240             avahi_server_set_errno(s, AVAHI_ERR_NOT_FOUND);
241             return NULL;
242         }
243
244         /* Update the entry */
245         old_record = e->record;
246         e->record = avahi_record_ref(r);
247         e->flags = flags;
248
249         /* Announce our changes when needed */
250         if (!avahi_record_equal_no_ttl(old_record, r) && (!g || g->state != AVAHI_ENTRY_GROUP_UNCOMMITED)) {
251
252             /* Remove the old entry from all caches, if needed */
253             if (!(e->flags & AVAHI_PUBLISH_UNIQUE))
254                 avahi_goodbye_entry(s, e, 1, 0);
255
256             /* Reannounce our updated entry */
257             avahi_reannounce_entry(s, e);
258         }
259
260         /* If we were the first entry in the list, we need to update the key */
261         if (is_first)
262             avahi_hashmap_replace(s->entries_by_key, e->record->key, e);
263         
264         avahi_record_unref(old_record);
265
266     } else {
267         AvahiEntry *t;
268
269         /* Add a new record */
270     
271         if (check_record_conflict(s, interface, protocol, r, flags) < 0) {
272             avahi_server_set_errno(s, AVAHI_ERR_COLLISION);
273             return NULL;
274         }
275
276         if (!(e = avahi_new(AvahiEntry, 1))) {
277             avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
278             return NULL;
279         }
280         
281         e->server = s;
282         e->record = avahi_record_ref(r);
283         e->group = g;
284         e->interface = interface;
285         e->protocol = protocol;
286         e->flags = flags;
287         e->dead = 0;
288         
289         AVAHI_LLIST_HEAD_INIT(AvahiAnnouncer, e->announcers);
290         
291         AVAHI_LLIST_PREPEND(AvahiEntry, entries, s->entries, e);
292         
293         /* Insert into hash table indexed by name */
294         t = avahi_hashmap_lookup(s->entries_by_key, e->record->key);
295         AVAHI_LLIST_PREPEND(AvahiEntry, by_key, t, e);
296         avahi_hashmap_replace(s->entries_by_key, e->record->key, t);
297         
298         /* Insert into group list */
299         if (g)
300             AVAHI_LLIST_PREPEND(AvahiEntry, by_group, g->entries, e); 
301         
302         avahi_announce_entry(s, e);
303     }
304
305     return e;
306 }
307
308 int avahi_server_add(
309     AvahiServer *s,
310     AvahiSEntryGroup *g,
311     AvahiIfIndex interface,
312     AvahiProtocol protocol,
313     AvahiPublishFlags flags,
314     AvahiRecord *r) {
315
316     if (!server_add_internal(s, g, interface, protocol, flags, r))
317         return avahi_server_errno(s);
318
319     return AVAHI_OK;
320 }
321
322 const AvahiRecord *avahi_server_iterate(AvahiServer *s, AvahiSEntryGroup *g, void **state) {
323     AvahiEntry **e = (AvahiEntry**) state;
324     assert(s);
325     assert(e);
326
327     if (!*e)
328         *e = g ? g->entries : s->entries;
329     
330     while (*e && (*e)->dead)
331         *e = g ? (*e)->by_group_next : (*e)->entries_next;
332         
333     if (!*e)
334         return NULL;
335
336     return avahi_record_ref((*e)->record);
337 }
338
339 int avahi_server_dump(AvahiServer *s, AvahiDumpCallback callback, void* userdata) {
340     AvahiEntry *e;
341     
342     assert(s);
343     assert(callback);
344
345     callback(";;; ZONE DUMP FOLLOWS ;;;", userdata);
346
347     for (e = s->entries; e; e = e->entries_next) {
348         char *t;
349         char ln[256];
350
351         if (e->dead)
352             continue;
353         
354         if (!(t = avahi_record_to_string(e->record)))
355             return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
356         
357         snprintf(ln, sizeof(ln), "%s ; iface=%i proto=%i", t, e->interface, e->protocol);
358         avahi_free(t);
359
360         callback(ln, userdata);
361     }
362
363     avahi_dump_caches(s->monitor, callback, userdata);
364
365     if (s->wide_area_lookup_engine)
366         avahi_wide_area_cache_dump(s->wide_area_lookup_engine, callback, userdata);
367     return AVAHI_OK;
368 }
369
370 static AvahiEntry *server_add_ptr_internal(
371     AvahiServer *s,
372     AvahiSEntryGroup *g,
373     AvahiIfIndex interface,
374     AvahiProtocol protocol,
375     AvahiPublishFlags flags,
376     uint32_t ttl,
377     const char *name,
378     const char *dest) {
379
380     AvahiRecord *r;
381     AvahiEntry *e;
382     
383     assert(s);
384     assert(dest);
385
386     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
387     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(dest), AVAHI_ERR_INVALID_HOST_NAME);
388
389     if (!name)
390         name = s->host_name_fqdn;
391
392     if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, ttl))) {
393         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
394         return NULL;
395     }
396         
397     r->data.ptr.name = avahi_normalize_name_strdup(dest);
398     e = server_add_internal(s, g, interface, protocol, flags, r);
399     avahi_record_unref(r);
400     return e;
401 }
402     
403 int avahi_server_add_ptr(
404     AvahiServer *s,
405     AvahiSEntryGroup *g,
406     AvahiIfIndex interface,
407     AvahiProtocol protocol,
408     AvahiPublishFlags flags,
409     uint32_t ttl,
410     const char *name,
411     const char *dest) {
412
413     AvahiEntry *e;
414
415     assert(s);
416
417     if (!(e = server_add_ptr_internal(s, g, interface, protocol, flags, ttl, name, dest)))
418         return avahi_server_errno(s);
419
420     return AVAHI_OK;
421 }
422
423 int avahi_server_add_address(
424     AvahiServer *s,
425     AvahiSEntryGroup *g,
426     AvahiIfIndex interface,
427     AvahiProtocol protocol,
428     AvahiPublishFlags flags,
429     const char *name,
430     AvahiAddress *a) {
431
432     char n[AVAHI_DOMAIN_NAME_MAX];
433     int ret = AVAHI_OK;
434     AvahiEntry *entry = NULL, *reverse = NULL;
435     AvahiRecord  *r;
436     
437     assert(s);
438     assert(a);
439
440     AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
441     AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(a->proto), AVAHI_ERR_INVALID_PROTOCOL);
442     AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags,
443                                               AVAHI_PUBLISH_NO_REVERSE|
444                                               AVAHI_PUBLISH_NO_ANNOUNCE|
445                                               AVAHI_PUBLISH_NO_PROBE|
446                                               AVAHI_PUBLISH_UPDATE|
447                                               AVAHI_PUBLISH_USE_WIDE_AREA|
448                                               AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
449     AVAHI_CHECK_VALIDITY(s, !name || avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
450
451     /* Prepare the host naem */
452     
453     if (!name)
454         name = s->host_name_fqdn;
455     else {
456         AVAHI_ASSERT_TRUE(avahi_normalize_name(name, n, sizeof(n)));
457         name = n;
458     }
459
460     transport_flags_from_domain(s, &flags, name);
461     AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
462     
463     /* Create the A/AAAA record */
464     
465     if (a->proto == AVAHI_PROTO_INET) {
466
467         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME))) {
468             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
469             goto finish;
470         }
471         
472         r->data.a.address = a->data.ipv4;
473         
474     } else {
475         assert(a->proto == AVAHI_PROTO_INET6);
476             
477         if (!(r = avahi_record_new_full(name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME))) {
478             ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
479             goto finish;
480         }
481         
482         r->data.aaaa.address = a->data.ipv6;
483     }
484         
485     entry = server_add_internal(s, g, interface, protocol, (flags & ~ AVAHI_PUBLISH_NO_REVERSE) | AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
486     avahi_record_unref(r);
487
488     if (!entry) {
489         ret = avahi_server_errno(s);
490         goto finish;
491     }
492
493     /* Create the reverse lookup entry */
494     
495     if (!(flags & AVAHI_PUBLISH_NO_REVERSE)) {
496         char reverse_n[AVAHI_DOMAIN_NAME_MAX];
497         avahi_reverse_lookup_name(a, reverse_n, sizeof(reverse_n));
498             
499         if (!(reverse = server_add_ptr_internal(s, g, interface, protocol, flags | AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL_HOST_NAME, reverse_n, name))) {
500             ret = avahi_server_errno(s);
501             goto finish;
502         }
503     }
504     
505 finish:
506
507     if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
508         if (entry)
509             avahi_entry_free(s, entry);
510         if (reverse)
511             avahi_entry_free(s, reverse);
512     }
513
514     return ret;
515 }
516
517 static AvahiEntry *server_add_txt_strlst_nocopy(
518     AvahiServer *s,
519     AvahiSEntryGroup *g,
520     AvahiIfIndex interface,
521     AvahiProtocol protocol,
522     AvahiPublishFlags flags,
523     uint32_t ttl,
524     const char *name,
525     AvahiStringList *strlst) {
526
527     AvahiRecord *r;
528     AvahiEntry *e;
529     
530     assert(s);
531
532     if (!(r = avahi_record_new_full(name ? name : s->host_name_fqdn, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, ttl))) {
533         avahi_string_list_free(strlst);
534         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
535         return NULL;
536     }
537     
538     r->data.txt.string_list = strlst;
539     e = server_add_internal(s, g, interface, protocol, flags, r);
540     avahi_record_unref(r);
541
542     return e;
543 }
544
545 static AvahiStringList *add_magic_cookie(
546     AvahiServer *s,
547     AvahiStringList *strlst) {
548
549     assert(s);
550
551     if (!s->config.add_service_cookie)
552         return strlst;
553
554     if (avahi_string_list_find(strlst, AVAHI_SERVICE_COOKIE))
555         /* This string list already contains a magic cookie */
556         return strlst;
557
558     return avahi_string_list_add_printf(strlst, AVAHI_SERVICE_COOKIE"=%u", s->local_service_cookie);
559 }
560
561 static int server_add_service_strlst_nocopy(
562     AvahiServer *s,
563     AvahiSEntryGroup *g,
564     AvahiIfIndex interface,
565     AvahiProtocol protocol,
566     AvahiPublishFlags flags,
567     const char *name,
568     const char *type,
569     const char *domain,
570     const char *host,
571     uint16_t port,
572     AvahiStringList *strlst) {
573
574     char ptr_name[AVAHI_DOMAIN_NAME_MAX], svc_name[AVAHI_DOMAIN_NAME_MAX], enum_ptr[AVAHI_DOMAIN_NAME_MAX], *h = NULL;
575     AvahiRecord *r = NULL;
576     int ret = AVAHI_OK;
577     AvahiEntry *srv_entry = NULL, *txt_entry = NULL, *ptr_entry = NULL, *enum_entry = NULL;
578     
579     assert(s);
580     assert(type);
581     assert(name);
582
583     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
584     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
585     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags,
586                                                                 AVAHI_PUBLISH_NO_COOKIE|
587                                                                 AVAHI_PUBLISH_UPDATE|
588                                                                 AVAHI_PUBLISH_USE_WIDE_AREA|
589                                                                 AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
590     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
591     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
592     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
593     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !host || avahi_is_valid_domain_name(host), AVAHI_ERR_INVALID_HOST_NAME);
594
595     if (!domain)
596         domain = s->domain_name;
597
598     if (!host)
599         host = s->host_name_fqdn;
600
601     transport_flags_from_domain(s, &flags, domain);
602     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
603     
604     if (!(h = avahi_normalize_name_strdup(host))) {
605         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
606         goto fail;
607     }
608
609     if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
610         (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, type, domain)) < 0 ||
611         (ret = avahi_service_name_join(enum_ptr, sizeof(enum_ptr), NULL, "_services._dns-sd._udp", domain)) < 0) {
612         avahi_server_set_errno(s, ret);
613         goto fail;
614     }
615
616     /* Add service enumeration PTR record */
617     
618     if (!(ptr_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name))) {
619         ret = avahi_server_errno(s);
620         goto fail;
621     }
622
623     /* Add SRV record */
624     
625     if (!(r = avahi_record_new_full(svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
626         ret = avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
627         goto fail;
628     }
629     
630     r->data.srv.priority = 0;
631     r->data.srv.weight = 0;
632     r->data.srv.port = port;
633     r->data.srv.name = h;
634     h = NULL;
635     srv_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, r);
636     avahi_record_unref(r);
637
638     if (!srv_entry) {
639         ret = avahi_server_errno(s);
640         goto fail;
641     }
642
643     /* Add TXT record */
644
645     if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
646         strlst = add_magic_cookie(s, strlst);
647     
648     txt_entry = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE, AVAHI_DEFAULT_TTL, svc_name, strlst);
649     strlst = NULL;
650
651     if (!txt_entry) {
652         ret = avahi_server_errno(s);
653         goto fail;
654     }
655
656     /* Add service type enumeration record */
657     
658     if (!(enum_entry = server_add_ptr_internal(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, enum_ptr, ptr_name))) {
659         ret = avahi_server_errno(s);
660         goto fail;
661     }
662
663 fail:
664     if (ret != AVAHI_OK && !(flags & AVAHI_PUBLISH_UPDATE)) {
665         if (srv_entry)
666             avahi_entry_free(s, srv_entry);
667         if (txt_entry)
668             avahi_entry_free(s, txt_entry);
669         if (ptr_entry)
670             avahi_entry_free(s, ptr_entry);
671         if (enum_entry)
672             avahi_entry_free(s, enum_entry);
673     }
674     
675     avahi_string_list_free(strlst);
676     avahi_free(h);
677     
678     return ret;
679 }
680
681 int avahi_server_add_service_strlst(
682     AvahiServer *s,
683     AvahiSEntryGroup *g,
684     AvahiIfIndex interface,
685     AvahiProtocol protocol,
686     AvahiPublishFlags flags,
687     const char *name,
688     const char *type,
689     const char *domain,
690     const char *host,
691     uint16_t port,
692     AvahiStringList *strlst) {
693
694     assert(s);
695     assert(type);
696     assert(name);
697
698     return server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_copy(strlst));
699 }
700
701 int avahi_server_add_service(
702     AvahiServer *s,
703     AvahiSEntryGroup *g,
704     AvahiIfIndex interface,
705     AvahiProtocol protocol,
706     AvahiPublishFlags flags,
707     const char *name,
708     const char *type,
709     const char *domain,
710     const char *host,
711     uint16_t port,
712     ... ){
713
714     va_list va;
715     int ret;
716     
717     va_start(va, port);
718     ret = server_add_service_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, host, port, avahi_string_list_new_va(va));
719     va_end(va);
720     
721     return ret;
722 }
723
724 static int server_update_service_txt_strlst_nocopy(
725     AvahiServer *s,
726     AvahiSEntryGroup *g,
727     AvahiIfIndex interface,
728     AvahiProtocol protocol,
729     AvahiPublishFlags flags,
730     const char *name,     
731     const char *type,     
732     const char *domain,   
733     AvahiStringList *strlst) {
734
735     char svc_name[AVAHI_DOMAIN_NAME_MAX];
736     int ret = AVAHI_OK;
737     AvahiEntry *e;
738     
739     assert(s);
740     assert(type);
741     assert(name);
742
743     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
744     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
745     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_FLAGS_VALID(flags,
746                                                                 AVAHI_PUBLISH_NO_COOKIE|
747                                                                 AVAHI_PUBLISH_USE_WIDE_AREA|
748                                                                 AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
749     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
750     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
751     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
752
753     if (!domain)
754         domain = s->domain_name;
755
756     transport_flags_from_domain(s, &flags, domain);
757     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
758
759     if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0) {
760         avahi_server_set_errno(s, ret);
761         goto fail;
762     }
763
764     /* Add TXT record */
765     if (!(flags & AVAHI_PUBLISH_NO_COOKIE))
766         strlst = add_magic_cookie(s, strlst);
767     
768     e = server_add_txt_strlst_nocopy(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_UPDATE, AVAHI_DEFAULT_TTL, svc_name, strlst);
769     strlst = NULL;
770
771     if (!e)
772         ret = avahi_server_errno(s);
773     
774 fail:
775     
776     avahi_string_list_free(strlst);
777     
778     return ret;
779 }
780
781 int avahi_server_update_service_txt_strlst(
782     AvahiServer *s,
783     AvahiSEntryGroup *g,
784     AvahiIfIndex interface,
785     AvahiProtocol protocol,
786     AvahiPublishFlags flags,
787     const char *name,     
788     const char *type,     
789     const char *domain,   
790     AvahiStringList *strlst) {
791
792     return server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_copy(strlst));
793 }
794
795 /** Update the TXT record for a service with the NULL termonate list of strings */
796 int avahi_server_update_service_txt(
797     AvahiServer *s,
798     AvahiSEntryGroup *g,
799     AvahiIfIndex interface,
800     AvahiProtocol protocol,
801     AvahiPublishFlags flags,
802     const char *name,     
803     const char *type,     
804     const char *domain,   
805     ...) {
806
807     va_list va;
808     int ret;
809     
810     va_start(va, domain);
811     ret = server_update_service_txt_strlst_nocopy(s, g, interface, protocol, flags, name, type, domain, avahi_string_list_new_va(va));
812     va_end(va);
813     
814     return ret;
815 }
816
817 int avahi_server_add_service_subtype(
818     AvahiServer *s,
819     AvahiSEntryGroup *g,
820     AvahiIfIndex interface,
821     AvahiProtocol protocol,
822     AvahiPublishFlags flags,
823     const char *name,        
824     const char *type,        
825     const char *domain,      
826     const char *subtype) {
827
828     int ret = AVAHI_OK;
829     char svc_name[AVAHI_DOMAIN_NAME_MAX], ptr_name[AVAHI_DOMAIN_NAME_MAX];
830     
831     assert(name);
832     assert(type);
833     assert(subtype);
834
835     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
836     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, AVAHI_PROTO_VALID(protocol), AVAHI_ERR_INVALID_PROTOCOL);
837     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);
838     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_name(name), AVAHI_ERR_INVALID_SERVICE_NAME);
839     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_type_strict(type), AVAHI_ERR_INVALID_SERVICE_TYPE);
840     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
841     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, avahi_is_valid_service_subtype(subtype), AVAHI_ERR_INVALID_SERVICE_SUBTYPE);
842
843     if (!domain)
844         domain = s->domain_name;
845
846     transport_flags_from_domain(s, &flags, domain);
847     AVAHI_CHECK_VALIDITY_SET_RET_GOTO_FAIL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
848
849     if ((ret = avahi_service_name_join(svc_name, sizeof(svc_name), name, type, domain)) < 0 ||
850         (ret = avahi_service_name_join(ptr_name, sizeof(ptr_name), NULL, subtype, domain)) < 0) {
851         avahi_server_set_errno(s, ret);
852         goto fail;
853     }
854
855     if ((ret = avahi_server_add_ptr(s, g, interface, protocol, 0, AVAHI_DEFAULT_TTL, ptr_name, svc_name)) < 0)
856         goto fail;
857
858 fail:
859     
860     return ret;
861 }
862
863 static void hexstring(char *s, size_t sl, const void *p, size_t pl) {
864     static const char hex[] = "0123456789abcdef";
865     int b = 0;
866     const uint8_t *k = p;
867
868     while (sl > 1 && pl > 0) {
869         *(s++) = hex[(b ? *k : *k >> 4) & 0xF];
870
871         if (b) {
872             k++;
873             pl--;
874         }
875         
876         b = !b;
877
878         sl--;
879     }
880
881     if (sl > 0)
882         *s = 0;
883 }
884
885 static AvahiEntry *server_add_dns_server_name(
886     AvahiServer *s,
887     AvahiSEntryGroup *g,
888     AvahiIfIndex interface,
889     AvahiProtocol protocol,
890     AvahiPublishFlags flags,
891     const char *domain,
892     AvahiDNSServerType type,
893     const char *name,
894     uint16_t port /** should be 53 */) {
895
896     AvahiEntry *e;
897     char t[AVAHI_DOMAIN_NAME_MAX], normalized_d[AVAHI_DOMAIN_NAME_MAX], *n;
898     
899     AvahiRecord *r;
900     
901     assert(s);
902     assert(name);
903
904     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_WIDE_AREA|AVAHI_PUBLISH_USE_MULTICAST), AVAHI_ERR_INVALID_FLAGS);
905     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
906     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, port != 0, AVAHI_ERR_INVALID_PORT);
907     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, avahi_is_valid_domain_name(name), AVAHI_ERR_INVALID_HOST_NAME);
908     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
909     
910     if (!domain)
911         domain = s->domain_name;
912
913     transport_flags_from_domain(s, &flags, domain);
914     AVAHI_CHECK_VALIDITY_RETURN_NULL(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
915
916     if (!(n = avahi_normalize_name_strdup(name))) {
917         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
918         return NULL;
919     }
920     
921     AVAHI_ASSERT_TRUE(avahi_normalize_name(domain, normalized_d, sizeof(normalized_d)));
922
923     snprintf(t, sizeof(t), "%s.%s", type == AVAHI_DNS_SERVER_RESOLVE ? "_domain._udp" : "_dns-update._udp", normalized_d);
924     
925     if (!(r = avahi_record_new_full(t, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME))) {
926         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
927         avahi_free(n);
928         return NULL;
929     }
930     
931     r->data.srv.priority = 0;
932     r->data.srv.weight = 0;
933     r->data.srv.port = port;
934     r->data.srv.name = n;
935     e = server_add_internal(s, g, interface, protocol, 0, r);
936     avahi_record_unref(r);
937
938     return e;
939 }
940
941 int avahi_server_add_dns_server_address(
942     AvahiServer *s,
943     AvahiSEntryGroup *g,
944     AvahiIfIndex interface,
945     AvahiProtocol protocol,
946     AvahiPublishFlags flags,
947     const char *domain,
948     AvahiDNSServerType type,
949     const AvahiAddress *address,
950     uint16_t port /** should be 53 */) {
951
952     AvahiRecord *r;
953     char n[64], h[64];
954     AvahiEntry *a_entry, *s_entry;
955
956     assert(s);
957     assert(address);
958
959     AVAHI_CHECK_VALIDITY(s, AVAHI_IF_VALID(interface), AVAHI_ERR_INVALID_INTERFACE);
960     AVAHI_CHECK_VALIDITY(s, AVAHI_PROTO_VALID(protocol) && AVAHI_PROTO_VALID(address->proto), AVAHI_ERR_INVALID_PROTOCOL);
961     AVAHI_CHECK_VALIDITY(s, AVAHI_FLAGS_VALID(flags, AVAHI_PUBLISH_USE_MULTICAST|AVAHI_PUBLISH_USE_WIDE_AREA), AVAHI_ERR_INVALID_FLAGS);
962     AVAHI_CHECK_VALIDITY(s, type == AVAHI_DNS_SERVER_UPDATE || type == AVAHI_DNS_SERVER_RESOLVE, AVAHI_ERR_INVALID_FLAGS);
963     AVAHI_CHECK_VALIDITY(s, port != 0, AVAHI_ERR_INVALID_PORT);
964     AVAHI_CHECK_VALIDITY(s, !domain || avahi_is_valid_domain_name(domain), AVAHI_ERR_INVALID_DOMAIN_NAME);
965
966     if (!domain)
967         domain = s->domain_name;
968
969     transport_flags_from_domain(s, &flags, domain);
970     AVAHI_CHECK_VALIDITY(s, flags & AVAHI_PUBLISH_USE_MULTICAST, AVAHI_ERR_NOT_SUPPORTED);
971     
972     if (address->proto == AVAHI_PROTO_INET) {
973         hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv4Address));
974         snprintf(n, sizeof(n), "ip-%s.%s", h, domain);
975         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, AVAHI_DEFAULT_TTL_HOST_NAME);
976         r->data.a.address = address->data.ipv4;
977     } else {
978         hexstring(h, sizeof(h), &address->data, sizeof(AvahiIPv6Address));
979         snprintf(n, sizeof(n), "ip6-%s.%s", h, domain);
980         r = avahi_record_new_full(n, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, AVAHI_DEFAULT_TTL_HOST_NAME);
981         r->data.aaaa.address = address->data.ipv6;
982     }
983
984     if (!r)
985         return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
986     
987     a_entry = server_add_internal(s, g, interface, protocol, AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_ALLOW_MULTIPLE, r);
988     avahi_record_unref(r);
989
990     if (!a_entry)
991         return avahi_server_errno(s);
992     
993     if (!(s_entry = server_add_dns_server_name(s, g, interface, protocol, flags, domain, type, n, port))) {
994         if (!(flags & AVAHI_PUBLISH_UPDATE))
995             avahi_entry_free(s, a_entry);
996         return avahi_server_errno(s);
997     }
998
999     return AVAHI_OK;
1000 }
1001
1002 void avahi_s_entry_group_change_state(AvahiSEntryGroup *g, AvahiEntryGroupState state) {
1003     assert(g);
1004
1005     if (g->state == state)
1006         return;
1007
1008     assert(state <= AVAHI_ENTRY_GROUP_COLLISION);
1009
1010     if (g->state == AVAHI_ENTRY_GROUP_ESTABLISHED) {
1011
1012         /* If the entry group was established for a time longer then
1013          * 5s, reset the establishment trial counter */
1014         
1015         if (avahi_age(&g->established_at) > 5000000)
1016             g->n_register_try = 0;
1017     }
1018     
1019     if (state == AVAHI_ENTRY_GROUP_ESTABLISHED)
1020
1021         /* If the entry group is now established, remember the time
1022          * this happened */
1023         
1024         gettimeofday(&g->established_at, NULL);
1025     
1026     g->state = state;
1027     
1028     if (g->callback)
1029         g->callback(g->server, g, state, g->userdata);
1030 }
1031
1032 AvahiSEntryGroup *avahi_s_entry_group_new(AvahiServer *s, AvahiSEntryGroupCallback callback, void* userdata) {
1033     AvahiSEntryGroup *g;
1034     
1035     assert(s);
1036
1037     if (!(g = avahi_new(AvahiSEntryGroup, 1))) {
1038         avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
1039         return NULL;
1040     }
1041     
1042     g->server = s;
1043     g->callback = callback;
1044     g->userdata = userdata;
1045     g->dead = 0;
1046     g->state = AVAHI_ENTRY_GROUP_UNCOMMITED;
1047     g->n_probing = 0;
1048     g->n_register_try = 0;
1049     g->register_time_event = NULL;
1050     g->register_time.tv_sec = 0;
1051     g->register_time.tv_usec = 0;
1052     AVAHI_LLIST_HEAD_INIT(AvahiEntry, g->entries);
1053
1054     AVAHI_LLIST_PREPEND(AvahiSEntryGroup, groups, s->groups, g);
1055     return g;
1056 }
1057
1058 void avahi_s_entry_group_free(AvahiSEntryGroup *g) {
1059     AvahiEntry *e;
1060     
1061     assert(g);
1062     assert(g->server);
1063
1064     for (e = g->entries; e; e = e->by_group_next) {
1065         if (!e->dead) {
1066             avahi_goodbye_entry(g->server, e, 1, 1);
1067             e->dead = 1;
1068         }
1069     }
1070
1071     if (g->register_time_event) {
1072         avahi_time_event_free(g->register_time_event);
1073         g->register_time_event = NULL;
1074     }
1075
1076     g->dead = 1;
1077     
1078     g->server->need_group_cleanup = 1;
1079     g->server->need_entry_cleanup = 1;
1080 }
1081
1082 static void entry_group_commit_real(AvahiSEntryGroup *g) {
1083     assert(g);
1084
1085     gettimeofday(&g->register_time, NULL);
1086
1087     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1088
1089     if (g->dead)
1090         return;
1091
1092     avahi_announce_group(g->server, g);
1093     avahi_s_entry_group_check_probed(g, 0);
1094 }
1095
1096 static void entry_group_register_time_event_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void* userdata) {
1097     AvahiSEntryGroup *g = userdata;
1098     assert(g);
1099
1100     avahi_time_event_free(g->register_time_event);
1101     g->register_time_event = NULL;
1102     
1103     /* Holdoff time passed, so let's start probing */
1104     entry_group_commit_real(g);
1105 }
1106
1107 int avahi_s_entry_group_commit(AvahiSEntryGroup *g) {
1108     struct timeval now;
1109     
1110     assert(g);
1111     assert(!g->dead);
1112
1113     if (g->state != AVAHI_ENTRY_GROUP_UNCOMMITED && g->state != AVAHI_ENTRY_GROUP_COLLISION)
1114         return avahi_server_set_errno(g->server, AVAHI_ERR_BAD_STATE);
1115
1116     g->n_register_try++;
1117
1118     avahi_timeval_add(&g->register_time,
1119                       1000*(g->n_register_try >= AVAHI_RR_RATE_LIMIT_COUNT ?
1120                             AVAHI_RR_HOLDOFF_MSEC_RATE_LIMIT :
1121                             AVAHI_RR_HOLDOFF_MSEC));
1122
1123     gettimeofday(&now, NULL);
1124
1125     if (avahi_timeval_compare(&g->register_time, &now) <= 0) {
1126
1127         /* Holdoff time passed, so let's start probing */
1128         entry_group_commit_real(g);
1129     } else {
1130
1131          /* Holdoff time has not yet passed, so let's wait */
1132         assert(!g->register_time_event);
1133         g->register_time_event = avahi_time_event_new(g->server->time_event_queue, &g->register_time, entry_group_register_time_event_callback, g);
1134         
1135         avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_REGISTERING);
1136     }
1137
1138     return AVAHI_OK;
1139 }
1140
1141 void avahi_s_entry_group_reset(AvahiSEntryGroup *g) {
1142     AvahiEntry *e;
1143     assert(g);
1144     
1145     for (e = g->entries; e; e = e->by_group_next) {
1146         if (!e->dead) {
1147             avahi_goodbye_entry(g->server, e, 1, 1);
1148             e->dead = 1;
1149         }
1150     }
1151     g->server->need_entry_cleanup = 1;
1152
1153     if (g->register_time_event) {
1154         avahi_time_event_free(g->register_time_event);
1155         g->register_time_event = NULL;
1156     }
1157     
1158     g->n_probing = 0;
1159
1160     gettimeofday(&g->register_time, NULL);
1161
1162     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_UNCOMMITED);
1163 }
1164
1165 int avahi_entry_is_commited(AvahiEntry *e) {
1166     assert(e);
1167     assert(!e->dead);
1168
1169     return !e->group ||
1170         e->group->state == AVAHI_ENTRY_GROUP_REGISTERING ||
1171         e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED;
1172 }
1173
1174 AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
1175     assert(g);
1176     assert(!g->dead);
1177
1178     return g->state;
1179 }
1180
1181 void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
1182     assert(g);
1183
1184     g->userdata = userdata;
1185 }
1186
1187 void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
1188     assert(g);
1189
1190     return g->userdata;
1191 }
1192
1193 int avahi_s_entry_group_is_empty(AvahiSEntryGroup *g) {
1194     AvahiEntry *e;
1195     assert(g);
1196
1197     /* Look for an entry that is not dead */
1198     for (e = g->entries; e; e = e->by_group_next)
1199         if (!e->dead)
1200             return 0;
1201
1202     return 1;
1203 }