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