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
55static 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
74void 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
101void 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
115void 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
148static 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
182static 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
313int 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
327const 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
344int 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
375static 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
408int 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
428int 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
510finish:
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
522static 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
550static 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
566static 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
668fail:
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
686int 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
706int 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
729static 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
779fail:
780
781    avahi_string_list_free(strlst);
782
783    return ret;
784}
785
786int 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 */
801int 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
822int 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
863fail:
864
865    return ret;
866}
867
868static 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
890static 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
946int 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
1007void 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
1042AvahiSEntryGroup *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
1068void 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
1092static 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
1106static 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
1117int 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
1154void 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
1171int 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
1180AvahiEntryGroupState avahi_s_entry_group_get_state(AvahiSEntryGroup *g) {
1181    assert(g);
1182    assert(!g->dead);
1183
1184    return g->state;
1185}
1186
1187void avahi_s_entry_group_set_data(AvahiSEntryGroup *g, void* userdata) {
1188    assert(g);
1189
1190    g->userdata = userdata;
1191}
1192
1193void* avahi_s_entry_group_get_data(AvahiSEntryGroup *g) {
1194    assert(g);
1195
1196    return g->userdata;
1197}
1198
1199int 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}
1210