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