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